From 17da3b5d82f4742a01cb1b88baa4c4701f39ed9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 9 Apr 2020 12:14:51 +0200 Subject: USD: ensure test does not depend on BLI_assert() The test failure in T75491 only showed up in debug builds because `BLI_assert()` is a no-op in release builds. This is now replaced by a proper GTests call to `ADD_FAILURE()`, ensuring that the test fails regardless of build mode. --- tests/gtests/usd/abstract_hierarchy_iterator_test.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc b/tests/gtests/usd/abstract_hierarchy_iterator_test.cc index e87ef547052..56d8100c30a 100644 --- a/tests/gtests/usd/abstract_hierarchy_iterator_test.cc +++ b/tests/gtests/usd/abstract_hierarchy_iterator_test.cc @@ -36,9 +36,11 @@ using namespace USD; class TestHierarchyWriter : public AbstractHierarchyWriter { public: + std::string writer_type; created_writers &writers_map; - TestHierarchyWriter(created_writers &writers_map) : writers_map(writers_map) + TestHierarchyWriter(const std::string &writer_type, created_writers &writers_map) + : writer_type(writer_type), writers_map(writers_map) { } @@ -47,7 +49,10 @@ class TestHierarchyWriter : public AbstractHierarchyWriter { const char *id_name = context.object->id.name; created_writers::mapped_type &writers = writers_map[id_name]; - BLI_assert(writers.find(context.export_path) == writers.end()); + if (writers.find(context.export_path) != writers.end()) { + ADD_FAILURE() << "Unexpectedly found another " << writer_type << " writer for " << id_name + << " to export to " << context.export_path; + } writers.insert(context.export_path); } }; @@ -81,19 +86,19 @@ class TestingHierarchyIterator : public AbstractHierarchyIterator { protected: AbstractHierarchyWriter *create_transform_writer(const HierarchyContext *context) override { - return new TestHierarchyWriter(transform_writers); + return new TestHierarchyWriter("transform", transform_writers); } AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) override { - return new TestHierarchyWriter(data_writers); + return new TestHierarchyWriter("data", data_writers); } AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) override { - return new TestHierarchyWriter(hair_writers); + return new TestHierarchyWriter("hair", hair_writers); } AbstractHierarchyWriter *create_particle_writer(const HierarchyContext *context) override { - return new TestHierarchyWriter(particle_writers); + return new TestHierarchyWriter("particle", particle_writers); } void delete_object_writer(AbstractHierarchyWriter *writer) override -- cgit v1.2.3 From a8bd385a5dcabbc55f69d885e64ccf6dc386a2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 9 Apr 2020 12:18:45 +0200 Subject: Fix T75491: ctest: usd_test fails in a debug configuration There was a bug in the `connect_loose_objects()` function, which caused some objects to be exported twice (once for real, and once transform-only). This is now resolved. --- .../io/usd/intern/abstract_hierarchy_iterator.cc | 26 +++++++++++++++------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc index 71cab020e57..d837d3d8dd3 100644 --- a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include extern "C" { @@ -259,24 +260,22 @@ void AbstractHierarchyIterator::connect_loose_objects() for (const ExportGraph::value_type &map_iter : loose_objects_graph) { const DupliAndDuplicator &export_info = map_iter.first; Object *object = export_info.first; - Object *export_parent = object->parent; while (true) { // Loose objects will all be real objects, as duplicated objects always have // their duplicator or other exported duplicated object as ancestor. - ExportGraph::iterator found_parent_iter = export_graph_.find( - std::make_pair(export_parent, nullptr)); - visit_object(object, export_parent, true); + ExportGraph::iterator found_parent_iter = export_graph_.find( + std::make_pair(object->parent, nullptr)); + visit_object(object, object->parent, true); if (found_parent_iter != export_graph_.end()) { break; } - // 'export_parent' will never be nullptr here, as the export graph contains the + // 'object->parent' will never be nullptr here, as the export graph contains the // tuple as root and thus will cause a break. - BLI_assert(export_parent != nullptr); + BLI_assert(object->parent != nullptr); - object = export_parent; - export_parent = export_parent->parent; + object = object->parent; } } } @@ -346,7 +345,18 @@ void AbstractHierarchyIterator::visit_object(Object *object, context->original_export_path = ""; copy_m4_m4(context->matrix_world, object->obmat); + // Store this HierarchyContext as child of the export parent. export_graph_[std::make_pair(export_parent, nullptr)].insert(context); + + // Create an empty entry for this object to indicate it is part of the export. This will be used + // by connect_loose_objects(). Having such an "indicator" will make it possible to do an O(log n) + // check on whether an object is part of the export, rather than having to check all objects in + // the map. Note that it's not possible to simply search for (object->parent, nullptr), as the + // object's parent in Blender may not be the same as its export-parent. + ExportGraph::key_type object_key = std::make_pair(object, nullptr); + if (export_graph_.find(object_key) == export_graph_.end()) { + export_graph_[object_key] = ExportChildren(); + } } void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, -- cgit v1.2.3 From a6d8f6c0f308edb74a2b56e784e49153e21c8680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Barschkis?= Date: Thu, 9 Apr 2020 12:18:05 +0200 Subject: Fluid: Fix unused variables Unsed variables for fluid caching. --- source/blender/blenkernel/intern/fluid.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index e96f65751de..8076cc0967b 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -3788,6 +3788,13 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *mmd, prev_particles = manta_has_particles(mds->fluid, mmd, scene_framenr - 1); prev_guide = manta_has_guiding(mds->fluid, mmd, scene_framenr - 1, guide_parent); + /* Unused for now, but needed for proper caching. */ + UNUSED_VARS(prev_guide); + UNUSED_VARS(next_noise); + UNUSED_VARS(next_mesh); + UNUSED_VARS(next_particles); + UNUSED_VARS(next_guide); + bool with_gdomain; with_gdomain = (mds->guide_source == FLUID_DOMAIN_GUIDE_SRC_DOMAIN); -- cgit v1.2.3 From 98abcfdb9db436dceaef9130eb2d15c8d9cb7783 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 9 Apr 2020 22:55:25 +1000 Subject: Cleanup: unused variable --- source/blender/editors/animation/anim_filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index c054691ca38..18e9d94a86c 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1840,7 +1840,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, bDopeSheet *ads = ac->ads; size_t items = 0; - Scene *scene = (Scene *)ads->source; + /* Scene *scene = (Scene *)ads->source; */ /* UNUSED */ ViewLayer *view_layer = (ViewLayer *)ac->view_layer; Base *base; -- cgit v1.2.3 From a7df9d6cdd767ec2f35386af6a714987d6c516af Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 9 Apr 2020 22:55:47 +1000 Subject: Fix menu search referencing image menu In this case it was called mask which wasn't correct since it's used for image & UV's, so rename the menu instead. --- release/scripts/startup/bl_ui/space_image.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 980f89eaaa4..4506307067e 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -703,7 +703,7 @@ class IMAGE_HT_header(Header): layout.prop(tool_settings, "uv_select_mode", text="", expand=True) layout.prop(uvedit, "sticky_select_mode", icon_only=True) - MASK_MT_editor_menus.draw_collapsible(context, layout) + IMAGE_MT_editor_menus.draw_collapsible(context, layout) layout.separator_spacer() @@ -749,8 +749,8 @@ class IMAGE_HT_header(Header): row.operator("image.play_composite", icon='PLAY') -class MASK_MT_editor_menus(Menu): - bl_idname = "MASK_MT_editor_menus" +class IMAGE_MT_editor_menus(Menu): + bl_idname = "IMAGE_MT_editor_menus" bl_label = "" def draw(self, context): @@ -1465,7 +1465,7 @@ classes = ( IMAGE_MT_uvs_snap_pie, IMAGE_HT_tool_header, IMAGE_HT_header, - MASK_MT_editor_menus, + IMAGE_MT_editor_menus, IMAGE_PT_active_tool, IMAGE_PT_mask, IMAGE_PT_mask_layers, -- cgit v1.2.3 From 464752876fb9c03dc5b0514674c9b67f547bd776 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 9 Apr 2020 14:28:20 +0200 Subject: Fix T75490: Child Of - difference between 2.82 and 2.83 rB10162d68e385 introduced a difference in computing the matrix, as well as a better way to compute the inverse matrix. This commit reverts the former, while keeping the latter and some other improvements. --- source/blender/blenkernel/intern/constraint.c | 35 +++++++++++++++++---------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index c91bf4e7f5b..099fdacf401 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -894,55 +894,64 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar } float parmat[4][4]; + float inverse_matrix[4][4]; /* Simple matrix parenting. */ if ((data->flag & CHILDOF_ALL) == CHILDOF_ALL) { copy_m4_m4(parmat, ct->matrix); + copy_m4_m4(inverse_matrix, data->invmat); } /* Filter the parent matrix by channel. */ else { float loc[3], eul[3], size[3]; + float loco[3], eulo[3], sizeo[3]; /* extract components of both matrices */ copy_v3_v3(loc, ct->matrix[3]); mat4_to_eulO(eul, ct->rotOrder, ct->matrix); mat4_to_size(size, ct->matrix); - /* disable channels not enabled */ + copy_v3_v3(loco, data->invmat[3]); + mat4_to_eulO(eulo, cob->rotOrder, data->invmat); + mat4_to_size(sizeo, data->invmat); + + /* Reset the locked channels to their no-op values. */ if (!(data->flag & CHILDOF_LOCX)) { - loc[0] = 0.0f; + loc[0] = loco[0] = 0.0f; } if (!(data->flag & CHILDOF_LOCY)) { - loc[1] = 0.0f; + loc[1] = loco[1] = 0.0f; } if (!(data->flag & CHILDOF_LOCZ)) { - loc[2] = 0.0f; + loc[2] = loco[2] = 0.0f; } if (!(data->flag & CHILDOF_ROTX)) { - eul[0] = 0.0f; + eul[0] = eulo[0] = 0.0f; } if (!(data->flag & CHILDOF_ROTY)) { - eul[1] = 0.0f; + eul[1] = eulo[1] = 0.0f; } if (!(data->flag & CHILDOF_ROTZ)) { - eul[2] = 0.0f; + eul[2] = eulo[2] = 0.0f; } if (!(data->flag & CHILDOF_SIZEX)) { - size[0] = 1.0f; + size[0] = sizeo[0] = 1.0f; } if (!(data->flag & CHILDOF_SIZEY)) { - size[1] = 1.0f; + size[1] = sizeo[1] = 1.0f; } if (!(data->flag & CHILDOF_SIZEZ)) { - size[2] = 1.0f; + size[2] = sizeo[2] = 1.0f; } - /* make new target mat and offset mat */ + /* Construct the new matrices given the disabled channels. */ loc_eulO_size_to_mat4(parmat, loc, eul, size, ct->rotOrder); + loc_eulO_size_to_mat4(inverse_matrix, loco, eulo, sizeo, cob->rotOrder); } - /* Compute the inverse matrix if requested. */ + /* If requested, compute the inverse matrix from the computed parent matrix. */ if (data->flag & CHILDOF_SET_INVERSE) { invert_m4_m4(data->invmat, parmat); + copy_m4_m4(inverse_matrix, data->invmat); data->flag &= ~CHILDOF_SET_INVERSE; @@ -962,7 +971,7 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar * (i.e. owner is 'parented' to parent). */ float orig_cob_matrix[4][4]; copy_m4_m4(orig_cob_matrix, cob->matrix); - mul_m4_series(cob->matrix, parmat, data->invmat, orig_cob_matrix); + mul_m4_series(cob->matrix, parmat, inverse_matrix, orig_cob_matrix); /* Without this, changes to scale and rotation can change location * of a parentless bone or a disconnected bone. Even though its set -- cgit v1.2.3 From bf68e1a54795bbe54badfbe6f97c2c2ee22c6e70 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 9 Apr 2020 16:25:46 +0200 Subject: Cleanup: Remove unused var This was not removed in previous fix. --- source/blender/editors/animation/anim_filter.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 18e9d94a86c..8bdf1ef7684 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1840,7 +1840,6 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, bDopeSheet *ads = ac->ads; size_t items = 0; - /* Scene *scene = (Scene *)ads->source; */ /* UNUSED */ ViewLayer *view_layer = (ViewLayer *)ac->view_layer; Base *base; -- cgit v1.2.3 From 862ec829422241878b3345661476d8551935aed2 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 9 Apr 2020 07:51:51 +0200 Subject: GPUViewport: Use GPUBatch for viewport drawing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When drawing the viewport to the screen the draw calls were not batched. This resulted in measurable slowdown on Windows Intel 10th gen platforms. This patch would cache the last draw calls per viewport. Our API does support partial redrawing of the viewport, but that isn't used anywhere. This patch does not include stereoscopy rendering. This still uses the imm approach and would still be slow on certain hardware. Reviewed By: ClĂ©ment Foucault Differential Revision: https://developer.blender.org/D7357 --- source/blender/gpu/intern/gpu_viewport.c | 131 ++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 28 deletions(-) diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index b2e1cb17946..fdbfa16a365 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -65,6 +65,24 @@ typedef struct ViewportTempTexture { GPUTexture *texture; } ViewportTempTexture; +/* Struct storing a viewport specific GPUBatch. + * The end-goal is to have a single batch shared across viewport and use a model matrix to place + * the batch. Due to OCIO and Image/UV editor we are not able to use an model matrix yet. */ +struct GPUViewportBatch { + GPUBatch *batch; + struct { + rctf rect_pos; + rctf rect_uv; + } last_used_parameters; +} GPUViewportBatch; + +static struct { + GPUVertFormat format; + struct { + uint pos, tex_coord; + } attr_id; +} g_viewport = {{0}}; + struct GPUViewport { int size[2]; int flag; @@ -97,6 +115,7 @@ struct GPUViewport { /* TODO(fclem) the uvimage display use the viewport but do not set any view transform for the * moment. The end goal would be to let the GPUViewport do the color management. */ bool do_color_management; + struct GPUViewportBatch batch; }; enum { @@ -625,6 +644,76 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_framebuffer_restore(); } +/* -------------------------------------------------------------------- */ +/** \name Viewport Batches + * \{ */ + +static GPUVertFormat *gpu_viewport_batch_format(void) +{ + if (g_viewport.format.attr_len == 0) { + GPUVertFormat *format = &g_viewport.format; + g_viewport.attr_id.pos = GPU_vertformat_attr_add( + format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + g_viewport.attr_id.tex_coord = GPU_vertformat_attr_add( + format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + } + return &g_viewport.format; +} + +static GPUBatch *gpu_viewport_batch_create(const rctf *rect_pos, const rctf *rect_uv) +{ + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(gpu_viewport_batch_format()); + const uint vbo_len = 4; + GPU_vertbuf_data_alloc(vbo, vbo_len); + + GPUVertBufRaw pos_step, tex_coord_step; + GPU_vertbuf_attr_get_raw_data(vbo, g_viewport.attr_id.pos, &pos_step); + GPU_vertbuf_attr_get_raw_data(vbo, g_viewport.attr_id.tex_coord, &tex_coord_step); + + copy_v2_fl2(GPU_vertbuf_raw_step(&pos_step), rect_pos->xmin, rect_pos->ymin); + copy_v2_fl2(GPU_vertbuf_raw_step(&tex_coord_step), rect_uv->xmin, rect_uv->ymin); + copy_v2_fl2(GPU_vertbuf_raw_step(&pos_step), rect_pos->xmax, rect_pos->ymin); + copy_v2_fl2(GPU_vertbuf_raw_step(&tex_coord_step), rect_uv->xmax, rect_uv->ymin); + copy_v2_fl2(GPU_vertbuf_raw_step(&pos_step), rect_pos->xmin, rect_pos->ymax); + copy_v2_fl2(GPU_vertbuf_raw_step(&tex_coord_step), rect_uv->xmin, rect_uv->ymax); + copy_v2_fl2(GPU_vertbuf_raw_step(&pos_step), rect_pos->xmax, rect_pos->ymax); + copy_v2_fl2(GPU_vertbuf_raw_step(&tex_coord_step), rect_uv->xmax, rect_uv->ymax); + + return GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); +} + +static GPUBatch *gpu_viewport_batch_get(GPUViewport *viewport, + const rctf *rect_pos, + const rctf *rect_uv) +{ + const float compare_limit = 0.0001f; + const bool parameters_changed = + (!BLI_rctf_compare( + &viewport->batch.last_used_parameters.rect_pos, rect_pos, compare_limit) || + !BLI_rctf_compare(&viewport->batch.last_used_parameters.rect_uv, rect_uv, compare_limit)); + + if (viewport->batch.batch && parameters_changed) { + GPU_batch_discard(viewport->batch.batch); + viewport->batch.batch = NULL; + } + + if (!viewport->batch.batch) { + viewport->batch.batch = gpu_viewport_batch_create(rect_pos, rect_uv); + viewport->batch.last_used_parameters.rect_pos = *rect_pos; + viewport->batch.last_used_parameters.rect_uv = *rect_uv; + } + return viewport->batch.batch; +} + +static void gpu_viewport_batch_free(GPUViewport *viewport) +{ + if (viewport->batch.batch) { + GPU_batch_discard(viewport->batch.batch); + viewport->batch.batch = NULL; + } +} + +/** \} */ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, const rctf *rect_pos, @@ -635,10 +724,6 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, GPUTexture *color = dtxl->color; GPUTexture *color_overlay = dtxl->color_overlay; - GPUVertFormat *vert_format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint texco = GPU_vertformat_attr_add(vert_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - bool use_ocio = false; if (viewport->do_color_management && display_colorspace) { @@ -650,38 +735,26 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, true); } - if (!use_ocio) { - immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE); - immUniform1i("display_transform", display_colorspace); - immUniform1i("image_texture", 0); - immUniform1i("overlays_texture", 1); + GPUBatch *batch = gpu_viewport_batch_get(viewport, rect_pos, rect_uv); + if (use_ocio) { + GPU_batch_program_set_imm_shader(batch); + } + else { + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE); + GPU_batch_uniform_1i(batch, "display_transform", display_colorspace); + GPU_batch_uniform_1i(batch, "image_texture", 0); + GPU_batch_uniform_1i(batch, "overlays_texture", 1); } GPU_texture_bind(color, 0); GPU_texture_bind(color_overlay, 1); - - immBegin(GPU_PRIM_TRI_STRIP, 4); - - immAttr2f(texco, rect_uv->xmin, rect_uv->ymin); - immVertex2f(pos, rect_pos->xmin, rect_pos->ymin); - immAttr2f(texco, rect_uv->xmax, rect_uv->ymin); - immVertex2f(pos, rect_pos->xmax, rect_pos->ymin); - immAttr2f(texco, rect_uv->xmin, rect_uv->ymax); - immVertex2f(pos, rect_pos->xmin, rect_pos->ymax); - immAttr2f(texco, rect_uv->xmax, rect_uv->ymax); - immVertex2f(pos, rect_pos->xmax, rect_pos->ymax); - - immEnd(); - + GPU_batch_draw(batch); GPU_texture_unbind(color); GPU_texture_unbind(color_overlay); if (use_ocio) { IMB_colormanagement_finish_glsl_draw(); } - else { - immUnbindProgram(); - } } /** @@ -745,8 +818,8 @@ void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, * Merge and draw the buffers of \a viewport into the currently active framebuffer, performing * color transform to display space. * - * \param rect: Coordinates to draw into. By swapping min and max values, drawing can be done with - * inversed axis coordinates (upside down or sideways). + * \param rect: Coordinates to draw into. By swapping min and max values, drawing can be done + * with inversed axis coordinates (upside down or sideways). */ void GPU_viewport_draw_to_screen(GPUViewport *viewport, int view, const rcti *rect) { @@ -923,5 +996,7 @@ void GPU_viewport_free(GPUViewport *viewport) DRW_instance_data_list_free(viewport->idatalist); MEM_freeN(viewport->idatalist); + gpu_viewport_batch_free(viewport); + MEM_freeN(viewport); } -- cgit v1.2.3 From 78f56d5582d71e001cc1326b7182aa39f9bdedec Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 9 Apr 2020 15:51:44 +0200 Subject: TaskScheduler: Minor Preparations for TBB Tasks: move priority from task to task pool {rBf7c18df4f599fe39ffc914e645e504fcdbee8636} Tasks: split task.c into task_pool.cc and task_iterator.c {rB4ada1d267749931ca934a74b14a82479bcaa92e0} Differential Revision: https://developer.blender.org/D7385 --- .../blender/blenkernel/intern/editmesh_tangent.c | 5 +- source/blender/blenkernel/intern/mesh_evaluate.c | 6 +- source/blender/blenkernel/intern/mesh_tangent.c | 5 +- source/blender/blenkernel/intern/ocean.c | 18 +- source/blender/blenkernel/intern/particle.c | 6 +- .../blenkernel/intern/particle_distribute.c | 6 +- source/blender/blenlib/BLI_task.h | 23 +- source/blender/blenlib/CMakeLists.txt | 3 +- source/blender/blenlib/intern/task.c | 1930 -------------------- source/blender/blenlib/intern/task_iterator.c | 925 ++++++++++ source/blender/blenlib/intern/task_pool.cc | 1029 +++++++++++ source/blender/depsgraph/intern/eval/deg_eval.cc | 5 +- .../blender/draw/intern/draw_cache_extract_mesh.c | 6 +- source/blender/editors/mesh/editmesh_undo.c | 6 +- source/blender/editors/render/render_opengl.c | 7 +- .../editors/sculpt_paint/paint_image_proj.c | 5 +- source/blender/editors/space_clip/clip_editor.c | 4 +- source/blender/editors/space_clip/clip_ops.c | 4 +- source/blender/editors/space_file/filelist.c | 13 +- source/blender/imbuf/intern/imageprocess.c | 13 +- tests/gtests/blenlib/BLI_linklist_lockfree_test.cc | 4 +- 21 files changed, 2021 insertions(+), 2002 deletions(-) delete mode 100644 source/blender/blenlib/intern/task.c create mode 100644 source/blender/blenlib/intern/task_iterator.c create mode 100644 source/blender/blenlib/intern/task_pool.cc diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.c index e291a68a4b1..c3ae2a54e13 100644 --- a/source/blender/blenkernel/intern/editmesh_tangent.c +++ b/source/blender/blenkernel/intern/editmesh_tangent.c @@ -364,7 +364,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, if (em->tottri != 0) { TaskScheduler *scheduler = BLI_task_scheduler_get(); TaskPool *task_pool; - task_pool = BLI_task_pool_create(scheduler, NULL); + task_pool = BLI_task_pool_create(scheduler, NULL, TASK_PRIORITY_LOW); tangent_mask_curr = 0; /* Calculate tangent layers */ @@ -417,8 +417,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em, mesh2tangent->looptris = (const BMLoop *(*)[3])em->looptris; mesh2tangent->tangent = loopdata_out->layers[index].data; - BLI_task_pool_push( - task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, NULL); } BLI_assert(tangent_mask_curr == tangent_mask); diff --git a/source/blender/blenkernel/intern/mesh_evaluate.c b/source/blender/blenkernel/intern/mesh_evaluate.c index 17fd7b18bab..e5be85b5ec7 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.c +++ b/source/blender/blenkernel/intern/mesh_evaluate.c @@ -1555,7 +1555,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common if (pool) { data_idx++; if (data_idx == LOOP_SPLIT_TASK_BLOCK_SIZE) { - BLI_task_pool_push(pool, loop_split_worker, data_buff, true, TASK_PRIORITY_LOW); + BLI_task_pool_push(pool, loop_split_worker, data_buff, true, NULL); data_idx = 0; } } @@ -1572,7 +1572,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common /* Last block of data... Since it is calloc'ed and we use first NULL item as stopper, * everything is fine. */ if (pool && data_idx) { - BLI_task_pool_push(pool, loop_split_worker, data_buff, true, TASK_PRIORITY_LOW); + BLI_task_pool_push(pool, loop_split_worker, data_buff, true, NULL); } if (edge_vectors) { @@ -1708,7 +1708,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts, TaskPool *task_pool; task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create(task_scheduler, &common_data); + task_pool = BLI_task_pool_create(task_scheduler, &common_data, TASK_PRIORITY_HIGH); loop_split_generator(task_pool, &common_data); diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c index ebc3e9c490a..a2a198cdb0d 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ b/source/blender/blenkernel/intern/mesh_tangent.c @@ -660,7 +660,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert, if (looptri_len != 0) { TaskScheduler *scheduler = BLI_task_scheduler_get(); TaskPool *task_pool; - task_pool = BLI_task_pool_create(scheduler, NULL); + task_pool = BLI_task_pool_create(scheduler, NULL, TASK_PRIORITY_LOW); tangent_mask_curr = 0; /* Calculate tangent layers */ @@ -707,8 +707,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert, } mesh2tangent->tangent = loopdata_out->layers[index].data; - BLI_task_pool_push( - task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, NULL); } BLI_assert(tangent_mask_curr == tangent_mask); diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 26485d10fbd..2683d384bc7 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -680,7 +680,7 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount osd.scale = scale; osd.chop_amount = chop_amount; - pool = BLI_task_pool_create(scheduler, &osd); + pool = BLI_task_pool_create(scheduler, &osd, TASK_PRIORITY_HIGH); BLI_rw_mutex_lock(&o->oceanmutex, THREAD_LOCK_WRITE); @@ -698,23 +698,23 @@ void BKE_ocean_simulate(struct Ocean *o, float t, float scale, float chop_amount BLI_task_parallel_range(0, o->_M, &osd, ocean_compute_htilda, &settings); if (o->_do_disp_y) { - BLI_task_pool_push(pool, ocean_compute_displacement_y, NULL, false, TASK_PRIORITY_HIGH); + BLI_task_pool_push(pool, ocean_compute_displacement_y, NULL, false, NULL); } if (o->_do_chop) { - BLI_task_pool_push(pool, ocean_compute_displacement_x, NULL, false, TASK_PRIORITY_HIGH); - BLI_task_pool_push(pool, ocean_compute_displacement_z, NULL, false, TASK_PRIORITY_HIGH); + BLI_task_pool_push(pool, ocean_compute_displacement_x, NULL, false, NULL); + BLI_task_pool_push(pool, ocean_compute_displacement_z, NULL, false, NULL); } if (o->_do_jacobian) { - BLI_task_pool_push(pool, ocean_compute_jacobian_jxx, NULL, false, TASK_PRIORITY_HIGH); - BLI_task_pool_push(pool, ocean_compute_jacobian_jzz, NULL, false, TASK_PRIORITY_HIGH); - BLI_task_pool_push(pool, ocean_compute_jacobian_jxz, NULL, false, TASK_PRIORITY_HIGH); + BLI_task_pool_push(pool, ocean_compute_jacobian_jxx, NULL, false, NULL); + BLI_task_pool_push(pool, ocean_compute_jacobian_jzz, NULL, false, NULL); + BLI_task_pool_push(pool, ocean_compute_jacobian_jxz, NULL, false, NULL); } if (o->_do_normals) { - BLI_task_pool_push(pool, ocean_compute_normal_x, NULL, false, TASK_PRIORITY_HIGH); - BLI_task_pool_push(pool, ocean_compute_normal_z, NULL, false, TASK_PRIORITY_HIGH); + BLI_task_pool_push(pool, ocean_compute_normal_x, NULL, false, NULL); + BLI_task_pool_push(pool, ocean_compute_normal_z, NULL, false, NULL); o->_N_y = 1.0f / scale; } diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 587dd5be2f2..0e35fa5d19f 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -2827,7 +2827,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim, } task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create(task_scheduler, &ctx); + task_pool = BLI_task_pool_create(task_scheduler, &ctx, TASK_PRIORITY_LOW); totchild = ctx.totchild; totparent = ctx.totparent; @@ -2850,7 +2850,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim, ParticleTask *task = &tasks_parent[i]; psys_task_init_path(task, sim); - BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, NULL); } BLI_task_pool_work_and_wait(task_pool); @@ -2861,7 +2861,7 @@ void psys_cache_child_paths(ParticleSimulationData *sim, ParticleTask *task = &tasks_child[i]; psys_task_init_path(task, sim); - BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, exec_child_path_cache, task, false, NULL); } BLI_task_pool_work_and_wait(task_pool); diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c index 9069f549e61..d91e27a92d8 100644 --- a/source/blender/blenkernel/intern/particle_distribute.c +++ b/source/blender/blenkernel/intern/particle_distribute.c @@ -1337,7 +1337,7 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) } task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create(task_scheduler, &ctx); + task_pool = BLI_task_pool_create(task_scheduler, &ctx, TASK_PRIORITY_LOW); totpart = (from == PART_FROM_CHILD ? sim->psys->totchild : sim->psys->totpart); psys_tasks_create(&ctx, 0, totpart, &tasks, &numtasks); @@ -1346,10 +1346,10 @@ static void distribute_particles_on_dm(ParticleSimulationData *sim, int from) psys_task_init_distribute(task, sim); if (from == PART_FROM_CHILD) { - BLI_task_pool_push(task_pool, exec_distribute_child, task, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, exec_distribute_child, task, false, NULL); } else { - BLI_task_pool_push(task_pool, exec_distribute_parent, task, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, exec_distribute_parent, task, false, NULL); } } BLI_task_pool_work_and_wait(task_pool); diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 83bcdff214d..898e4be5f92 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -73,27 +73,25 @@ typedef struct TaskPool TaskPool; typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata, int threadid); typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata, int threadid); -TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata); -TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, void *userdata); -TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler, void *userdata); +TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, + void *userdata, + TaskPriority priority); +TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler, + void *userdata, + TaskPriority priority); void BLI_task_pool_free(TaskPool *pool); -void BLI_task_pool_push_ex(TaskPool *pool, - TaskRunFunction run, - void *taskdata, - bool free_taskdata, - TaskFreeFunction freedata, - TaskPriority priority); void BLI_task_pool_push(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, - TaskPriority priority); + TaskFreeFunction freedata); void BLI_task_pool_push_from_thread(TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, - TaskPriority priority, + TaskFreeFunction freedata, int thread_id); /* work and wait until all tasks are done */ @@ -112,6 +110,9 @@ void *BLI_task_pool_userdata(TaskPool *pool); /* optional mutex to use from run function */ ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool); +/* Thread ID of thread that created the task pool. */ +int BLI_task_pool_creator_thread_id(TaskPool *pool); + /* Delayed push, use that to reduce thread overhead by accumulating * all new tasks into local queue first and pushing it to scheduler * from within a single mutex lock. diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 5f5145cab70..bdfe3dd4747 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -118,7 +118,8 @@ set(SRC intern/string_utf8.c intern/string_utils.c intern/system.c - intern/task.c + intern/task_pool.cc + intern/task_iterator.c intern/threads.c intern/time.c intern/timecode.c diff --git a/source/blender/blenlib/intern/task.c b/source/blender/blenlib/intern/task.c deleted file mode 100644 index 293275c402d..00000000000 --- a/source/blender/blenlib/intern/task.c +++ /dev/null @@ -1,1930 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup bli - * - * A generic task system which can be used for any task based subsystem. - */ - -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_listBase.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_mempool.h" -#include "BLI_task.h" -#include "BLI_threads.h" - -#include "atomic_ops.h" - -/* Define this to enable some detailed statistic print. */ -#undef DEBUG_STATS - -/* Types */ - -/* Number of per-thread pre-allocated tasks. - * - * For more details see description of TaskMemPool. - */ -#define MEMPOOL_SIZE 256 - -/* Number of tasks which are pushed directly to local thread queue. - * - * This allows thread to fetch next task without locking the whole queue. - */ -#define LOCAL_QUEUE_SIZE 1 - -/* Number of tasks which are allowed to be scheduled in a delayed manner. - * - * This allows to use less locks per graph node children schedule. More details - * could be found at TaskThreadLocalStorage::do_delayed_push. - */ -#define DELAYED_QUEUE_SIZE 4096 - -#ifndef NDEBUG -# define ASSERT_THREAD_ID(scheduler, thread_id) \ - do { \ - if (!BLI_thread_is_main()) { \ - TaskThread *thread = pthread_getspecific(scheduler->tls_id_key); \ - if (thread == NULL) { \ - BLI_assert(thread_id == 0); \ - } \ - else { \ - BLI_assert(thread_id == thread->id); \ - } \ - } \ - else { \ - BLI_assert(thread_id == 0); \ - } \ - } while (false) -#else -# define ASSERT_THREAD_ID(scheduler, thread_id) -#endif - -typedef struct Task { - struct Task *next, *prev; - - TaskRunFunction run; - void *taskdata; - bool free_taskdata; - TaskFreeFunction freedata; - TaskPool *pool; -} Task; - -/* This is a per-thread storage of pre-allocated tasks. - * - * The idea behind this is simple: reduce amount of malloc() calls when pushing - * new task to the pool. This is done by keeping memory from the tasks which - * were finished already, so instead of freeing that memory we put it to the - * pool for the later re-use. - * - * The tricky part here is to avoid any inter-thread synchronization, hence no - * lock must exist around this pool. The pool will become an owner of the pointer - * from freed task, and only corresponding thread will be able to use this pool - * (no memory stealing and such). - * - * This leads to the following use of the pool: - * - * - task_push() should provide proper thread ID from which the task is being - * pushed from. - * - * - Task allocation function which check corresponding memory pool and if there - * is any memory in there it'll mark memory as re-used, remove it from the pool - * and use that memory for the new task. - * - * At this moment task queue owns the memory. - * - * - When task is done and task_free() is called the memory will be put to the - * pool which corresponds to a thread which handled the task. - */ -typedef struct TaskMemPool { - /* Number of pre-allocated tasks in the pool. */ - int num_tasks; - /* Pre-allocated task memory pointers. */ - Task *tasks[MEMPOOL_SIZE]; -} TaskMemPool; - -#ifdef DEBUG_STATS -typedef struct TaskMemPoolStats { - /* Number of allocations. */ - int num_alloc; - /* Number of avoided allocations (pointer was re-used from the pool). */ - int num_reuse; - /* Number of discarded memory due to pool saturation, */ - int num_discard; -} TaskMemPoolStats; -#endif - -typedef struct TaskThreadLocalStorage { - /* Memory pool for faster task allocation. - * The idea is to re-use memory of finished/discarded tasks by this thread. - */ - TaskMemPool task_mempool; - - /* Local queue keeps thread alive by keeping small amount of tasks ready - * to be picked up without causing global thread locks for synchronization. - */ - int num_local_queue; - Task *local_queue[LOCAL_QUEUE_SIZE]; - - /* Thread can be marked for delayed tasks push. This is helpful when it's - * know that lots of subsequent task pushed will happen from the same thread - * without "interrupting" for task execution. - * - * We try to accumulate as much tasks as possible in a local queue without - * any locks first, and then we push all of them into a scheduler's queue - * from within a single mutex lock. - */ - bool do_delayed_push; - int num_delayed_queue; - Task *delayed_queue[DELAYED_QUEUE_SIZE]; -} TaskThreadLocalStorage; - -struct TaskPool { - TaskScheduler *scheduler; - - volatile size_t num; - ThreadMutex num_mutex; - ThreadCondition num_cond; - - void *userdata; - ThreadMutex user_mutex; - - volatile bool do_cancel; - volatile bool do_work; - - volatile bool is_suspended; - bool start_suspended; - ListBase suspended_queue; - size_t num_suspended; - - /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler - * has to use its special background fallback thread in case we are in - * single-threaded situation. - */ - bool run_in_background; - - /* This is a task scheduler's ID of a thread at which pool was constructed. - * It will be used to access task TLS. - */ - int thread_id; - - /* For the pools which are created from non-main thread which is not a - * scheduler worker thread we can't re-use any of scheduler's threads TLS - * and have to use our own one. - */ - bool use_local_tls; - TaskThreadLocalStorage local_tls; -#ifndef NDEBUG - pthread_t creator_thread_id; -#endif - -#ifdef DEBUG_STATS - TaskMemPoolStats *mempool_stats; -#endif -}; - -struct TaskScheduler { - pthread_t *threads; - struct TaskThread *task_threads; - int num_threads; - bool background_thread_only; - - ListBase queue; - ThreadMutex queue_mutex; - ThreadCondition queue_cond; - - ThreadMutex startup_mutex; - ThreadCondition startup_cond; - volatile int num_thread_started; - - volatile bool do_exit; - - /* NOTE: In pthread's TLS we store the whole TaskThread structure. */ - pthread_key_t tls_id_key; -}; - -typedef struct TaskThread { - TaskScheduler *scheduler; - int id; - TaskThreadLocalStorage tls; -} TaskThread; - -/* Helper */ -BLI_INLINE void task_data_free(Task *task, const int thread_id) -{ - if (task->free_taskdata) { - if (task->freedata) { - task->freedata(task->pool, task->taskdata, thread_id); - } - else { - MEM_freeN(task->taskdata); - } - } -} - -BLI_INLINE void initialize_task_tls(TaskThreadLocalStorage *tls) -{ - memset(tls, 0, sizeof(TaskThreadLocalStorage)); -} - -BLI_INLINE TaskThreadLocalStorage *get_task_tls(TaskPool *pool, const int thread_id) -{ - TaskScheduler *scheduler = pool->scheduler; - BLI_assert(thread_id >= 0); - BLI_assert(thread_id <= scheduler->num_threads); - if (pool->use_local_tls && thread_id == 0) { - BLI_assert(pool->thread_id == 0); - BLI_assert(!BLI_thread_is_main()); - BLI_assert(pthread_equal(pthread_self(), pool->creator_thread_id)); - return &pool->local_tls; - } - if (thread_id == 0) { - BLI_assert(BLI_thread_is_main()); - return &scheduler->task_threads[pool->thread_id].tls; - } - return &scheduler->task_threads[thread_id].tls; -} - -BLI_INLINE void free_task_tls(TaskThreadLocalStorage *tls) -{ - TaskMemPool *task_mempool = &tls->task_mempool; - for (int i = 0; i < task_mempool->num_tasks; i++) { - MEM_freeN(task_mempool->tasks[i]); - } -} - -static Task *task_alloc(TaskPool *pool, const int thread_id) -{ - BLI_assert(thread_id <= pool->scheduler->num_threads); - if (thread_id != -1) { - BLI_assert(thread_id >= 0); - BLI_assert(thread_id <= pool->scheduler->num_threads); - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - TaskMemPool *task_mempool = &tls->task_mempool; - /* Try to re-use task memory from a thread local storage. */ - if (task_mempool->num_tasks > 0) { - --task_mempool->num_tasks; - /* Success! We've just avoided task allocation. */ -#ifdef DEBUG_STATS - pool->mempool_stats[thread_id].num_reuse++; -#endif - return task_mempool->tasks[task_mempool->num_tasks]; - } - /* We are doomed to allocate new task data. */ -#ifdef DEBUG_STATS - pool->mempool_stats[thread_id].num_alloc++; -#endif - } - return MEM_mallocN(sizeof(Task), "New task"); -} - -static void task_free(TaskPool *pool, Task *task, const int thread_id) -{ - task_data_free(task, thread_id); - BLI_assert(thread_id >= 0); - BLI_assert(thread_id <= pool->scheduler->num_threads); - if (thread_id == 0) { - BLI_assert(pool->use_local_tls || BLI_thread_is_main()); - } - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - TaskMemPool *task_mempool = &tls->task_mempool; - if (task_mempool->num_tasks < MEMPOOL_SIZE - 1) { - /* Successfully allowed the task to be re-used later. */ - task_mempool->tasks[task_mempool->num_tasks] = task; - ++task_mempool->num_tasks; - } - else { - /* Local storage saturated, no other way than just discard - * the memory. - * - * TODO(sergey): We can perhaps store such pointer in a global - * scheduler pool, maybe it'll be faster than discarding and - * allocating again. - */ - MEM_freeN(task); -#ifdef DEBUG_STATS - pool->mempool_stats[thread_id].num_discard++; -#endif - } -} - -/* Task Scheduler */ - -static void task_pool_num_decrease(TaskPool *pool, size_t done) -{ - BLI_mutex_lock(&pool->num_mutex); - - BLI_assert(pool->num >= done); - - pool->num -= done; - - if (pool->num == 0) { - BLI_condition_notify_all(&pool->num_cond); - } - - BLI_mutex_unlock(&pool->num_mutex); -} - -static void task_pool_num_increase(TaskPool *pool, size_t new) -{ - BLI_mutex_lock(&pool->num_mutex); - - pool->num += new; - BLI_condition_notify_all(&pool->num_cond); - - BLI_mutex_unlock(&pool->num_mutex); -} - -static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task) -{ - bool found_task = false; - BLI_mutex_lock(&scheduler->queue_mutex); - - while (!scheduler->queue.first && !scheduler->do_exit) { - BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex); - } - - do { - Task *current_task; - - /* Assuming we can only have a void queue in 'exit' case here seems logical - * (we should only be here after our worker thread has been woken up from a - * condition_wait(), which only happens after a new task was added to the queue), - * but it is wrong. - * Waiting on condition may wake up the thread even if condition is not signaled - * (spurious wake-ups), and some race condition may also empty the queue **after** - * condition has been signaled, but **before** awoken thread reaches this point... - * See http://stackoverflow.com/questions/8594591 - * - * So we only abort here if do_exit is set. - */ - if (scheduler->do_exit) { - BLI_mutex_unlock(&scheduler->queue_mutex); - return false; - } - - for (current_task = scheduler->queue.first; current_task != NULL; - current_task = current_task->next) { - TaskPool *pool = current_task->pool; - - if (scheduler->background_thread_only && !pool->run_in_background) { - continue; - } - - *task = current_task; - found_task = true; - BLI_remlink(&scheduler->queue, *task); - break; - } - if (!found_task) { - BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex); - } - } while (!found_task); - - BLI_mutex_unlock(&scheduler->queue_mutex); - - return true; -} - -BLI_INLINE void handle_local_queue(TaskThreadLocalStorage *tls, const int thread_id) -{ - BLI_assert(!tls->do_delayed_push); - while (tls->num_local_queue > 0) { - /* We pop task from queue before handling it so handler of the task can - * push next job to the local queue. - */ - tls->num_local_queue--; - Task *local_task = tls->local_queue[tls->num_local_queue]; - /* TODO(sergey): Double-check work_and_wait() doesn't handle other's - * pool tasks. - */ - TaskPool *local_pool = local_task->pool; - local_task->run(local_pool, local_task->taskdata, thread_id); - task_free(local_pool, local_task, thread_id); - } - BLI_assert(!tls->do_delayed_push); -} - -static void *task_scheduler_thread_run(void *thread_p) -{ - TaskThread *thread = (TaskThread *)thread_p; - TaskThreadLocalStorage *tls = &thread->tls; - TaskScheduler *scheduler = thread->scheduler; - int thread_id = thread->id; - Task *task; - - pthread_setspecific(scheduler->tls_id_key, thread); - - /* signal the main thread when all threads have started */ - BLI_mutex_lock(&scheduler->startup_mutex); - scheduler->num_thread_started++; - if (scheduler->num_thread_started == scheduler->num_threads) { - BLI_condition_notify_one(&scheduler->startup_cond); - } - BLI_mutex_unlock(&scheduler->startup_mutex); - - /* keep popping off tasks */ - while (task_scheduler_thread_wait_pop(scheduler, &task)) { - TaskPool *pool = task->pool; - - /* run task */ - BLI_assert(!tls->do_delayed_push); - task->run(pool, task->taskdata, thread_id); - BLI_assert(!tls->do_delayed_push); - - /* delete task */ - task_free(pool, task, thread_id); - - /* Handle all tasks from local queue. */ - handle_local_queue(tls, thread_id); - - /* notify pool task was done */ - task_pool_num_decrease(pool, 1); - } - - return NULL; -} - -TaskScheduler *BLI_task_scheduler_create(int num_threads) -{ - TaskScheduler *scheduler = MEM_callocN(sizeof(TaskScheduler), "TaskScheduler"); - - /* multiple places can use this task scheduler, sharing the same - * threads, so we keep track of the number of users. */ - scheduler->do_exit = false; - - BLI_listbase_clear(&scheduler->queue); - BLI_mutex_init(&scheduler->queue_mutex); - BLI_condition_init(&scheduler->queue_cond); - - BLI_mutex_init(&scheduler->startup_mutex); - BLI_condition_init(&scheduler->startup_cond); - scheduler->num_thread_started = 0; - - if (num_threads == 0) { - /* automatic number of threads will be main thread + num cores */ - num_threads = BLI_system_thread_count(); - } - - /* main thread will also work, so we count it too */ - num_threads -= 1; - - /* Add background-only thread if needed. */ - if (num_threads == 0) { - scheduler->background_thread_only = true; - num_threads = 1; - } - - scheduler->task_threads = MEM_mallocN(sizeof(TaskThread) * (num_threads + 1), - "TaskScheduler task threads"); - - /* Initialize TLS for main thread. */ - initialize_task_tls(&scheduler->task_threads[0].tls); - - pthread_key_create(&scheduler->tls_id_key, NULL); - - /* launch threads that will be waiting for work */ - if (num_threads > 0) { - int i; - - scheduler->num_threads = num_threads; - scheduler->threads = MEM_callocN(sizeof(pthread_t) * num_threads, "TaskScheduler threads"); - - for (i = 0; i < num_threads; i++) { - TaskThread *thread = &scheduler->task_threads[i + 1]; - thread->scheduler = scheduler; - thread->id = i + 1; - initialize_task_tls(&thread->tls); - - if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) { - fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads); - } - } - } - - /* Wait for all worker threads to start before returning to caller to prevent the case where - * threads are still starting and pthread_join is called, which causes a deadlock on pthreads4w. - */ - BLI_mutex_lock(&scheduler->startup_mutex); - /* NOTE: Use loop here to avoid false-positive everything-is-ready caused by spontaneous thread - * wake up. */ - while (scheduler->num_thread_started != num_threads) { - BLI_condition_wait(&scheduler->startup_cond, &scheduler->startup_mutex); - } - BLI_mutex_unlock(&scheduler->startup_mutex); - - return scheduler; -} - -void BLI_task_scheduler_free(TaskScheduler *scheduler) -{ - Task *task; - - /* stop all waiting threads */ - BLI_mutex_lock(&scheduler->queue_mutex); - scheduler->do_exit = true; - BLI_condition_notify_all(&scheduler->queue_cond); - BLI_mutex_unlock(&scheduler->queue_mutex); - - pthread_key_delete(scheduler->tls_id_key); - - /* delete threads */ - if (scheduler->threads) { - int i; - - for (i = 0; i < scheduler->num_threads; i++) { - if (pthread_join(scheduler->threads[i], NULL) != 0) { - fprintf(stderr, "TaskScheduler failed to join thread %d/%d\n", i, scheduler->num_threads); - } - } - - MEM_freeN(scheduler->threads); - } - - /* Delete task thread data */ - if (scheduler->task_threads) { - for (int i = 0; i < scheduler->num_threads + 1; i++) { - TaskThreadLocalStorage *tls = &scheduler->task_threads[i].tls; - free_task_tls(tls); - } - - MEM_freeN(scheduler->task_threads); - } - - /* delete leftover tasks */ - for (task = scheduler->queue.first; task; task = task->next) { - task_data_free(task, 0); - } - BLI_freelistN(&scheduler->queue); - - /* delete mutex/condition */ - BLI_mutex_end(&scheduler->queue_mutex); - BLI_condition_end(&scheduler->queue_cond); - BLI_mutex_end(&scheduler->startup_mutex); - BLI_condition_end(&scheduler->startup_cond); - - MEM_freeN(scheduler); -} - -int BLI_task_scheduler_num_threads(TaskScheduler *scheduler) -{ - return scheduler->num_threads + 1; -} - -static void task_scheduler_push(TaskScheduler *scheduler, Task *task, TaskPriority priority) -{ - task_pool_num_increase(task->pool, 1); - - /* add task to queue */ - BLI_mutex_lock(&scheduler->queue_mutex); - - if (priority == TASK_PRIORITY_HIGH) { - BLI_addhead(&scheduler->queue, task); - } - else { - BLI_addtail(&scheduler->queue, task); - } - - BLI_condition_notify_one(&scheduler->queue_cond); - BLI_mutex_unlock(&scheduler->queue_mutex); -} - -static void task_scheduler_push_all(TaskScheduler *scheduler, - TaskPool *pool, - Task **tasks, - int num_tasks) -{ - if (num_tasks == 0) { - return; - } - - task_pool_num_increase(pool, num_tasks); - - BLI_mutex_lock(&scheduler->queue_mutex); - - for (int i = 0; i < num_tasks; i++) { - BLI_addhead(&scheduler->queue, tasks[i]); - } - - BLI_condition_notify_all(&scheduler->queue_cond); - BLI_mutex_unlock(&scheduler->queue_mutex); -} - -static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool) -{ - Task *task, *nexttask; - size_t done = 0; - - BLI_mutex_lock(&scheduler->queue_mutex); - - /* free all tasks from this pool from the queue */ - for (task = scheduler->queue.first; task; task = nexttask) { - nexttask = task->next; - - if (task->pool == pool) { - task_data_free(task, pool->thread_id); - BLI_freelinkN(&scheduler->queue, task); - - done++; - } - } - - BLI_mutex_unlock(&scheduler->queue_mutex); - - /* notify done */ - task_pool_num_decrease(pool, done); -} - -/* Task Pool */ - -static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, - void *userdata, - const bool is_background, - const bool is_suspended) -{ - TaskPool *pool = MEM_mallocN(sizeof(TaskPool), "TaskPool"); - -#ifndef NDEBUG - /* Assert we do not try to create a background pool from some parent task - - * those only work OK from main thread. */ - if (is_background) { - const pthread_t thread_id = pthread_self(); - int i = scheduler->num_threads; - - while (i--) { - BLI_assert(!pthread_equal(scheduler->threads[i], thread_id)); - } - } -#endif - - pool->scheduler = scheduler; - pool->num = 0; - pool->do_cancel = false; - pool->do_work = false; - pool->is_suspended = is_suspended; - pool->start_suspended = is_suspended; - pool->num_suspended = 0; - pool->suspended_queue.first = pool->suspended_queue.last = NULL; - pool->run_in_background = is_background; - pool->use_local_tls = false; - - BLI_mutex_init(&pool->num_mutex); - BLI_condition_init(&pool->num_cond); - - pool->userdata = userdata; - BLI_mutex_init(&pool->user_mutex); - - if (BLI_thread_is_main()) { - pool->thread_id = 0; - } - else { - TaskThread *thread = pthread_getspecific(scheduler->tls_id_key); - if (thread == NULL) { - /* NOTE: Task pool is created from non-main thread which is not - * managed by the task scheduler. We identify ourselves as thread ID - * 0 but we do not use scheduler's TLS storage and use our own - * instead to avoid any possible threading conflicts. - */ - pool->thread_id = 0; - pool->use_local_tls = true; -#ifndef NDEBUG - pool->creator_thread_id = pthread_self(); -#endif - initialize_task_tls(&pool->local_tls); - } - else { - pool->thread_id = thread->id; - } - } - -#ifdef DEBUG_STATS - pool->mempool_stats = MEM_callocN(sizeof(*pool->mempool_stats) * (scheduler->num_threads + 1), - "per-taskpool mempool stats"); -#endif - - /* Ensure malloc will go fine from threads, - * - * This is needed because we could be in main thread here - * and malloc could be non-thread safe at this point because - * no other jobs are running. - */ - BLI_threaded_malloc_begin(); - - return pool; -} - -/** - * Create a normal task pool. Tasks will be executed as soon as they are added. - */ -TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata) -{ - return task_pool_create_ex(scheduler, userdata, false, false); -} - -/** - * Create a background task pool. - * In multi-threaded context, there is no differences with #BLI_task_pool_create(), - * but in single-threaded case it is ensured to have at least one worker thread to run on - * (i.e. you don't have to call #BLI_task_pool_work_and_wait - * on it to be sure it will be processed). - * - * \note Background pools are non-recursive - * (that is, you should not create other background pools in tasks assigned to a background pool, - * they could end never being executed, since the 'fallback' background thread is already - * busy with parent task in single-threaded context). - */ -TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, void *userdata) -{ - return task_pool_create_ex(scheduler, userdata, true, false); -} - -/** - * Similar to BLI_task_pool_create() but does not schedule any tasks for execution - * for until BLI_task_pool_work_and_wait() is called. This helps reducing threading - * overhead when pushing huge amount of small initial tasks from the main thread. - */ -TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler, void *userdata) -{ - return task_pool_create_ex(scheduler, userdata, false, true); -} - -void BLI_task_pool_free(TaskPool *pool) -{ - BLI_task_pool_cancel(pool); - - BLI_mutex_end(&pool->num_mutex); - BLI_condition_end(&pool->num_cond); - - BLI_mutex_end(&pool->user_mutex); - -#ifdef DEBUG_STATS - printf("Thread ID Allocated Reused Discarded\n"); - for (int i = 0; i < pool->scheduler->num_threads + 1; i++) { - printf("%02d %05d %05d %05d\n", - i, - pool->mempool_stats[i].num_alloc, - pool->mempool_stats[i].num_reuse, - pool->mempool_stats[i].num_discard); - } - MEM_freeN(pool->mempool_stats); -#endif - - if (pool->use_local_tls) { - free_task_tls(&pool->local_tls); - } - - MEM_freeN(pool); - - BLI_threaded_malloc_end(); -} - -BLI_INLINE bool task_can_use_local_queues(TaskPool *pool, int thread_id) -{ - return (thread_id != -1 && (thread_id != pool->thread_id || pool->do_work)); -} - -static void task_pool_push(TaskPool *pool, - TaskRunFunction run, - void *taskdata, - bool free_taskdata, - TaskFreeFunction freedata, - TaskPriority priority, - int thread_id) -{ - /* Allocate task and fill it's properties. */ - Task *task = task_alloc(pool, thread_id); - task->run = run; - task->taskdata = taskdata; - task->free_taskdata = free_taskdata; - task->freedata = freedata; - task->pool = pool; - /* For suspended pools we put everything yo a global queue first - * and exit as soon as possible. - * - * This tasks will be moved to actual execution when pool is - * activated by work_and_wait(). - */ - if (pool->is_suspended) { - BLI_addhead(&pool->suspended_queue, task); - atomic_fetch_and_add_z(&pool->num_suspended, 1); - return; - } - /* Populate to any local queue first, this is cheapest push ever. */ - if (task_can_use_local_queues(pool, thread_id)) { - ASSERT_THREAD_ID(pool->scheduler, thread_id); - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - /* Try to push to a local execution queue. - * These tasks will be picked up next. - */ - if (tls->num_local_queue < LOCAL_QUEUE_SIZE) { - tls->local_queue[tls->num_local_queue] = task; - tls->num_local_queue++; - return; - } - /* If we are in the delayed tasks push mode, we push tasks to a - * temporary local queue first without any locks, and then move them - * to global execution queue with a single lock. - */ - if (tls->do_delayed_push && tls->num_delayed_queue < DELAYED_QUEUE_SIZE) { - tls->delayed_queue[tls->num_delayed_queue] = task; - tls->num_delayed_queue++; - return; - } - } - /* Do push to a global execution pool, slowest possible method, - * causes quite reasonable amount of threading overhead. - */ - task_scheduler_push(pool->scheduler, task, priority); -} - -void BLI_task_pool_push_ex(TaskPool *pool, - TaskRunFunction run, - void *taskdata, - bool free_taskdata, - TaskFreeFunction freedata, - TaskPriority priority) -{ - task_pool_push(pool, run, taskdata, free_taskdata, freedata, priority, -1); -} - -void BLI_task_pool_push( - TaskPool *pool, TaskRunFunction run, void *taskdata, bool free_taskdata, TaskPriority priority) -{ - BLI_task_pool_push_ex(pool, run, taskdata, free_taskdata, NULL, priority); -} - -void BLI_task_pool_push_from_thread(TaskPool *pool, - TaskRunFunction run, - void *taskdata, - bool free_taskdata, - TaskPriority priority, - int thread_id) -{ - task_pool_push(pool, run, taskdata, free_taskdata, NULL, priority, thread_id); -} - -void BLI_task_pool_work_and_wait(TaskPool *pool) -{ - TaskThreadLocalStorage *tls = get_task_tls(pool, pool->thread_id); - TaskScheduler *scheduler = pool->scheduler; - - if (atomic_fetch_and_and_uint8((uint8_t *)&pool->is_suspended, 0)) { - if (pool->num_suspended) { - task_pool_num_increase(pool, pool->num_suspended); - BLI_mutex_lock(&scheduler->queue_mutex); - - BLI_movelisttolist(&scheduler->queue, &pool->suspended_queue); - - BLI_condition_notify_all(&scheduler->queue_cond); - BLI_mutex_unlock(&scheduler->queue_mutex); - - pool->num_suspended = 0; - } - } - - pool->do_work = true; - - ASSERT_THREAD_ID(pool->scheduler, pool->thread_id); - - handle_local_queue(tls, pool->thread_id); - - BLI_mutex_lock(&pool->num_mutex); - - while (pool->num != 0) { - Task *task, *work_task = NULL; - bool found_task = false; - - BLI_mutex_unlock(&pool->num_mutex); - - BLI_mutex_lock(&scheduler->queue_mutex); - - /* find task from this pool. if we get a task from another pool, - * we can get into deadlock */ - - for (task = scheduler->queue.first; task; task = task->next) { - if (task->pool == pool) { - work_task = task; - found_task = true; - BLI_remlink(&scheduler->queue, task); - break; - } - } - - BLI_mutex_unlock(&scheduler->queue_mutex); - - /* if found task, do it, otherwise wait until other tasks are done */ - if (found_task) { - /* run task */ - BLI_assert(!tls->do_delayed_push); - work_task->run(pool, work_task->taskdata, pool->thread_id); - BLI_assert(!tls->do_delayed_push); - - /* delete task */ - task_free(pool, task, pool->thread_id); - - /* Handle all tasks from local queue. */ - handle_local_queue(tls, pool->thread_id); - - /* notify pool task was done */ - task_pool_num_decrease(pool, 1); - } - - BLI_mutex_lock(&pool->num_mutex); - if (pool->num == 0) { - break; - } - - if (!found_task) { - BLI_condition_wait(&pool->num_cond, &pool->num_mutex); - } - } - - BLI_mutex_unlock(&pool->num_mutex); - - BLI_assert(tls->num_local_queue == 0); -} - -void BLI_task_pool_work_wait_and_reset(TaskPool *pool) -{ - BLI_task_pool_work_and_wait(pool); - - pool->do_work = false; - pool->is_suspended = pool->start_suspended; -} - -void BLI_task_pool_cancel(TaskPool *pool) -{ - pool->do_cancel = true; - - task_scheduler_clear(pool->scheduler, pool); - - /* wait until all entries are cleared */ - BLI_mutex_lock(&pool->num_mutex); - while (pool->num) { - BLI_condition_wait(&pool->num_cond, &pool->num_mutex); - } - BLI_mutex_unlock(&pool->num_mutex); - - pool->do_cancel = false; -} - -bool BLI_task_pool_canceled(TaskPool *pool) -{ - return pool->do_cancel; -} - -void *BLI_task_pool_userdata(TaskPool *pool) -{ - return pool->userdata; -} - -ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool) -{ - return &pool->user_mutex; -} - -void BLI_task_pool_delayed_push_begin(TaskPool *pool, int thread_id) -{ - if (task_can_use_local_queues(pool, thread_id)) { - ASSERT_THREAD_ID(pool->scheduler, thread_id); - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - tls->do_delayed_push = true; - } -} - -void BLI_task_pool_delayed_push_end(TaskPool *pool, int thread_id) -{ - if (task_can_use_local_queues(pool, thread_id)) { - ASSERT_THREAD_ID(pool->scheduler, thread_id); - TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); - BLI_assert(tls->do_delayed_push); - task_scheduler_push_all(pool->scheduler, pool, tls->delayed_queue, tls->num_delayed_queue); - tls->do_delayed_push = false; - tls->num_delayed_queue = 0; - } -} - -/* Parallel range routines */ - -/** - * - * Main functions: - * - #BLI_task_parallel_range - * - #BLI_task_parallel_listbase (#ListBase - double linked list) - * - * TODO: - * - #BLI_task_parallel_foreach_link (#Link - single linked list) - * - #BLI_task_parallel_foreach_ghash/gset (#GHash/#GSet - hash & set) - * - #BLI_task_parallel_foreach_mempool (#BLI_mempool - iterate over mempools) - */ - -/* Allows to avoid using malloc for userdata_chunk in tasks, when small enough. */ -#define MALLOCA(_size) ((_size) <= 8192) ? alloca((_size)) : MEM_mallocN((_size), __func__) -#define MALLOCA_FREE(_mem, _size) \ - if (((_mem) != NULL) && ((_size) > 8192)) \ - MEM_freeN((_mem)) - -/* Stores all needed data to perform a parallelized iteration, - * with a same operation (callback function). - * It can be chained with other tasks in a single-linked list way. */ -typedef struct TaskParallelRangeState { - struct TaskParallelRangeState *next; - - /* Start and end point of integer value iteration. */ - int start, stop; - - /* User-defined data, shared between all worker threads. */ - void *userdata_shared; - /* User-defined callback function called for each value in [start, stop[ specified range. */ - TaskParallelRangeFunc func; - - /* Each instance of looping chunks will get a copy of this data - * (similar to OpenMP's firstprivate). - */ - void *initial_tls_memory; /* Pointer to actual user-defined 'tls' data. */ - size_t tls_data_size; /* Size of that data. */ - - void *flatten_tls_storage; /* 'tls' copies of initial_tls_memory for each running task. */ - /* Number of 'tls' copies in the array, i.e. number of worker threads. */ - size_t num_elements_in_tls_storage; - - /* Function called from calling thread once whole range have been processed. */ - TaskParallelFinalizeFunc func_finalize; - - /* Current value of the iterator, shared between all threads (atomically updated). */ - int iter_value; - int iter_chunk_num; /* Amount of iterations to process in a single step. */ -} TaskParallelRangeState; - -/* Stores all the parallel tasks for a single pool. */ -typedef struct TaskParallelRangePool { - /* The workers' task pool. */ - TaskPool *pool; - /* The number of worker tasks we need to create. */ - int num_tasks; - /* The total number of iterations in all the added ranges. */ - int num_total_iters; - /* The size (number of items) processed at once by a worker task. */ - int chunk_size; - - /* Linked list of range tasks to process. */ - TaskParallelRangeState *parallel_range_states; - /* Current range task beeing processed, swapped atomically. */ - TaskParallelRangeState *current_state; - /* Scheduling settings common to all tasks. */ - TaskParallelSettings *settings; -} TaskParallelRangePool; - -BLI_INLINE void task_parallel_calc_chunk_size(const TaskParallelSettings *settings, - const int tot_items, - int num_tasks, - int *r_chunk_size) -{ - int chunk_size = 0; - - if (!settings->use_threading) { - /* Some users of this helper will still need a valid chunk size in case processing is not - * threaded. We can use a bigger one than in default threaded case then. */ - chunk_size = 1024; - num_tasks = 1; - } - else if (settings->min_iter_per_thread > 0) { - /* Already set by user, no need to do anything here. */ - chunk_size = settings->min_iter_per_thread; - } - else { - /* Multiplier used in heuristics below to define "optimal" chunk size. - * The idea here is to increase the chunk size to compensate for a rather measurable threading - * overhead caused by fetching tasks. With too many CPU threads we are starting - * to spend too much time in those overheads. - * First values are: 1 if num_tasks < 16; - * else 2 if num_tasks < 32; - * else 3 if num_tasks < 48; - * else 4 if num_tasks < 64; - * etc. - * Note: If we wanted to keep the 'power of two' multiplier, we'd need something like: - * 1 << max_ii(0, (int)(sizeof(int) * 8) - 1 - bitscan_reverse_i(num_tasks) - 3) - */ - const int num_tasks_factor = max_ii(1, num_tasks >> 3); - - /* We could make that 'base' 32 number configurable in TaskParallelSettings too, or maybe just - * always use that heuristic using TaskParallelSettings.min_iter_per_thread as basis? */ - chunk_size = 32 * num_tasks_factor; - - /* Basic heuristic to avoid threading on low amount of items. - * We could make that limit configurable in settings too. */ - if (tot_items > 0 && tot_items < max_ii(256, chunk_size * 2)) { - chunk_size = tot_items; - } - } - - BLI_assert(chunk_size > 0); - - if (tot_items > 0) { - switch (settings->scheduling_mode) { - case TASK_SCHEDULING_STATIC: - *r_chunk_size = max_ii(chunk_size, tot_items / num_tasks); - break; - case TASK_SCHEDULING_DYNAMIC: - *r_chunk_size = chunk_size; - break; - } - } - else { - /* If total amount of items is unknown, we can only use dynamic scheduling. */ - *r_chunk_size = chunk_size; - } -} - -BLI_INLINE void task_parallel_range_calc_chunk_size(TaskParallelRangePool *range_pool) -{ - int num_iters = 0; - int min_num_iters = INT_MAX; - for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; - state = state->next) { - const int ni = state->stop - state->start; - num_iters += ni; - if (min_num_iters > ni) { - min_num_iters = ni; - } - } - range_pool->num_total_iters = num_iters; - /* Note: Passing min_num_iters here instead of num_iters kind of partially breaks the 'static' - * scheduling, but pooled range iterator is inherently non-static anyway, so adding a small level - * of dynamic scheduling here should be fine. */ - task_parallel_calc_chunk_size( - range_pool->settings, min_num_iters, range_pool->num_tasks, &range_pool->chunk_size); -} - -BLI_INLINE bool parallel_range_next_iter_get(TaskParallelRangePool *__restrict range_pool, - int *__restrict r_iter, - int *__restrict r_count, - TaskParallelRangeState **__restrict r_state) -{ - /* We need an atomic op here as well to fetch the initial state, since some other thread might - * have already updated it. */ - TaskParallelRangeState *current_state = atomic_cas_ptr( - (void **)&range_pool->current_state, NULL, NULL); - - int previter = INT32_MAX; - - while (current_state != NULL && previter >= current_state->stop) { - previter = atomic_fetch_and_add_int32(¤t_state->iter_value, range_pool->chunk_size); - *r_iter = previter; - *r_count = max_ii(0, min_ii(range_pool->chunk_size, current_state->stop - previter)); - - if (previter >= current_state->stop) { - /* At this point the state we got is done, we need to go to the next one. In case some other - * thread already did it, then this does nothing, and we'll just get current valid state - * at start of the next loop. */ - TaskParallelRangeState *current_state_from_atomic_cas = atomic_cas_ptr( - (void **)&range_pool->current_state, current_state, current_state->next); - - if (current_state == current_state_from_atomic_cas) { - /* The atomic CAS operation was successful, we did update range_pool->current_state, so we - * can safely switch to next state. */ - current_state = current_state->next; - } - else { - /* The atomic CAS operation failed, but we still got range_pool->current_state value out of - * it, just use it as our new current state. */ - current_state = current_state_from_atomic_cas; - } - } - } - - *r_state = current_state; - return (current_state != NULL && previter < current_state->stop); -} - -static void parallel_range_func(TaskPool *__restrict pool, void *tls_data_idx, int thread_id) -{ - TaskParallelRangePool *__restrict range_pool = BLI_task_pool_userdata(pool); - TaskParallelTLS tls = { - .thread_id = thread_id, - .userdata_chunk = NULL, - }; - TaskParallelRangeState *state; - int iter, count; - while (parallel_range_next_iter_get(range_pool, &iter, &count, &state)) { - tls.userdata_chunk = (char *)state->flatten_tls_storage + - (((size_t)POINTER_AS_INT(tls_data_idx)) * state->tls_data_size); - for (int i = 0; i < count; i++) { - state->func(state->userdata_shared, iter + i, &tls); - } - } -} - -static void parallel_range_single_thread(TaskParallelRangePool *range_pool) -{ - for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; - state = state->next) { - const int start = state->start; - const int stop = state->stop; - void *userdata = state->userdata_shared; - TaskParallelRangeFunc func = state->func; - - void *initial_tls_memory = state->initial_tls_memory; - const size_t tls_data_size = state->tls_data_size; - void *flatten_tls_storage = NULL; - const bool use_tls_data = (tls_data_size != 0) && (initial_tls_memory != NULL); - if (use_tls_data) { - flatten_tls_storage = MALLOCA(tls_data_size); - memcpy(flatten_tls_storage, initial_tls_memory, tls_data_size); - } - TaskParallelTLS tls = { - .thread_id = 0, - .userdata_chunk = flatten_tls_storage, - }; - for (int i = start; i < stop; i++) { - func(userdata, i, &tls); - } - if (state->func_finalize != NULL) { - state->func_finalize(userdata, flatten_tls_storage); - } - MALLOCA_FREE(flatten_tls_storage, tls_data_size); - } -} - -/** - * This function allows to parallelized for loops in a similar way to OpenMP's - * 'parallel for' statement. - * - * See public API doc of ParallelRangeSettings for description of all settings. - */ -void BLI_task_parallel_range(const int start, - const int stop, - void *userdata, - TaskParallelRangeFunc func, - TaskParallelSettings *settings) -{ - if (start == stop) { - return; - } - - BLI_assert(start < stop); - - TaskParallelRangeState state = { - .next = NULL, - .start = start, - .stop = stop, - .userdata_shared = userdata, - .func = func, - .iter_value = start, - .initial_tls_memory = settings->userdata_chunk, - .tls_data_size = settings->userdata_chunk_size, - .func_finalize = settings->func_finalize, - }; - TaskParallelRangePool range_pool = { - .pool = NULL, .parallel_range_states = &state, .current_state = NULL, .settings = settings}; - int i, num_threads, num_tasks; - - void *tls_data = settings->userdata_chunk; - const size_t tls_data_size = settings->userdata_chunk_size; - if (tls_data_size != 0) { - BLI_assert(tls_data != NULL); - } - const bool use_tls_data = (tls_data_size != 0) && (tls_data != NULL); - void *flatten_tls_storage = NULL; - - /* If it's not enough data to be crunched, don't bother with tasks at all, - * do everything from the current thread. - */ - if (!settings->use_threading) { - parallel_range_single_thread(&range_pool); - return; - } - - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - num_threads = BLI_task_scheduler_num_threads(task_scheduler); - - /* The idea here is to prevent creating task for each of the loop iterations - * and instead have tasks which are evenly distributed across CPU cores and - * pull next iter to be crunched using the queue. - */ - range_pool.num_tasks = num_tasks = num_threads + 2; - - task_parallel_range_calc_chunk_size(&range_pool); - range_pool.num_tasks = num_tasks = min_ii(num_tasks, - max_ii(1, (stop - start) / range_pool.chunk_size)); - - if (num_tasks == 1) { - parallel_range_single_thread(&range_pool); - return; - } - - TaskPool *task_pool = range_pool.pool = BLI_task_pool_create_suspended(task_scheduler, - &range_pool); - - range_pool.current_state = &state; - - if (use_tls_data) { - state.flatten_tls_storage = flatten_tls_storage = MALLOCA(tls_data_size * (size_t)num_tasks); - state.tls_data_size = tls_data_size; - } - - for (i = 0; i < num_tasks; i++) { - if (use_tls_data) { - void *userdata_chunk_local = (char *)flatten_tls_storage + (tls_data_size * (size_t)i); - memcpy(userdata_chunk_local, tls_data, tls_data_size); - } - /* Use this pool's pre-allocated tasks. */ - BLI_task_pool_push_from_thread(task_pool, - parallel_range_func, - POINTER_FROM_INT(i), - false, - TASK_PRIORITY_HIGH, - task_pool->thread_id); - } - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - - if (use_tls_data) { - if (settings->func_finalize != NULL) { - for (i = 0; i < num_tasks; i++) { - void *userdata_chunk_local = (char *)flatten_tls_storage + (tls_data_size * (size_t)i); - settings->func_finalize(userdata, userdata_chunk_local); - } - } - MALLOCA_FREE(flatten_tls_storage, tls_data_size * (size_t)num_tasks); - } -} - -/** - * Initialize a task pool to parallelize several for loops at the same time. - * - * See public API doc of ParallelRangeSettings for description of all settings. - * Note that loop-specific settings (like 'tls' data or finalize function) must be left NULL here. - * Only settings controlling how iteration is parallelized must be defined, as those will affect - * all loops added to that pool. - */ -TaskParallelRangePool *BLI_task_parallel_range_pool_init(const TaskParallelSettings *settings) -{ - TaskParallelRangePool *range_pool = MEM_callocN(sizeof(*range_pool), __func__); - - BLI_assert(settings->userdata_chunk == NULL); - BLI_assert(settings->func_finalize == NULL); - range_pool->settings = MEM_mallocN(sizeof(*range_pool->settings), __func__); - *range_pool->settings = *settings; - - return range_pool; -} - -/** - * Add a loop task to the pool. It does not execute it at all. - * - * See public API doc of ParallelRangeSettings for description of all settings. - * Note that only 'tls'-related data are used here. - */ -void BLI_task_parallel_range_pool_push(TaskParallelRangePool *range_pool, - const int start, - const int stop, - void *userdata, - TaskParallelRangeFunc func, - const TaskParallelSettings *settings) -{ - BLI_assert(range_pool->pool == NULL); - - if (start == stop) { - return; - } - - BLI_assert(start < stop); - if (settings->userdata_chunk_size != 0) { - BLI_assert(settings->userdata_chunk != NULL); - } - - TaskParallelRangeState *state = MEM_callocN(sizeof(*state), __func__); - state->start = start; - state->stop = stop; - state->userdata_shared = userdata; - state->func = func; - state->iter_value = start; - state->initial_tls_memory = settings->userdata_chunk; - state->tls_data_size = settings->userdata_chunk_size; - state->func_finalize = settings->func_finalize; - - state->next = range_pool->parallel_range_states; - range_pool->parallel_range_states = state; -} - -static void parallel_range_func_finalize(TaskPool *__restrict pool, - void *v_state, - int UNUSED(thread_id)) -{ - TaskParallelRangePool *__restrict range_pool = BLI_task_pool_userdata(pool); - TaskParallelRangeState *state = v_state; - - for (int i = 0; i < range_pool->num_tasks; i++) { - void *tls_data = (char *)state->flatten_tls_storage + (state->tls_data_size * (size_t)i); - state->func_finalize(state->userdata_shared, tls_data); - } -} - -/** - * Run all tasks pushed to the range_pool. - * - * Note that the range pool is re-usable (you may push new tasks into it and call this function - * again). - */ -void BLI_task_parallel_range_pool_work_and_wait(TaskParallelRangePool *range_pool) -{ - BLI_assert(range_pool->pool == NULL); - - /* If it's not enough data to be crunched, don't bother with tasks at all, - * do everything from the current thread. - */ - if (!range_pool->settings->use_threading) { - parallel_range_single_thread(range_pool); - return; - } - - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); - - /* The idea here is to prevent creating task for each of the loop iterations - * and instead have tasks which are evenly distributed across CPU cores and - * pull next iter to be crunched using the queue. - */ - int num_tasks = num_threads + 2; - range_pool->num_tasks = num_tasks; - - task_parallel_range_calc_chunk_size(range_pool); - range_pool->num_tasks = num_tasks = min_ii( - num_tasks, max_ii(1, range_pool->num_total_iters / range_pool->chunk_size)); - - if (num_tasks == 1) { - parallel_range_single_thread(range_pool); - return; - } - - /* We create all 'tls' data here in a single loop. */ - for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; - state = state->next) { - void *userdata_chunk = state->initial_tls_memory; - const size_t userdata_chunk_size = state->tls_data_size; - if (userdata_chunk_size == 0) { - BLI_assert(userdata_chunk == NULL); - continue; - } - - void *userdata_chunk_array = NULL; - state->flatten_tls_storage = userdata_chunk_array = MALLOCA(userdata_chunk_size * - (size_t)num_tasks); - for (int i = 0; i < num_tasks; i++) { - void *userdata_chunk_local = (char *)userdata_chunk_array + - (userdata_chunk_size * (size_t)i); - memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); - } - } - - TaskPool *task_pool = range_pool->pool = BLI_task_pool_create_suspended(task_scheduler, - range_pool); - - range_pool->current_state = range_pool->parallel_range_states; - - for (int i = 0; i < num_tasks; i++) { - BLI_task_pool_push_from_thread(task_pool, - parallel_range_func, - POINTER_FROM_INT(i), - false, - TASK_PRIORITY_HIGH, - task_pool->thread_id); - } - - BLI_task_pool_work_and_wait(task_pool); - - BLI_assert(atomic_cas_ptr((void **)&range_pool->current_state, NULL, NULL) == NULL); - - /* Finalize all tasks. */ - for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; - state = state->next) { - const size_t userdata_chunk_size = state->tls_data_size; - void *userdata_chunk_array = state->flatten_tls_storage; - UNUSED_VARS_NDEBUG(userdata_chunk_array); - if (userdata_chunk_size == 0) { - BLI_assert(userdata_chunk_array == NULL); - continue; - } - - if (state->func_finalize != NULL) { - BLI_task_pool_push_from_thread(task_pool, - parallel_range_func_finalize, - state, - false, - TASK_PRIORITY_HIGH, - task_pool->thread_id); - } - } - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - range_pool->pool = NULL; - - /* Cleanup all tasks. */ - TaskParallelRangeState *state_next; - for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; - state = state_next) { - state_next = state->next; - - const size_t userdata_chunk_size = state->tls_data_size; - void *userdata_chunk_array = state->flatten_tls_storage; - if (userdata_chunk_size != 0) { - BLI_assert(userdata_chunk_array != NULL); - MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * (size_t)num_tasks); - } - - MEM_freeN(state); - } - range_pool->parallel_range_states = NULL; -} - -/** - * Clear/free given \a range_pool. - */ -void BLI_task_parallel_range_pool_free(TaskParallelRangePool *range_pool) -{ - TaskParallelRangeState *state_next = NULL; - for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; - state = state_next) { - state_next = state->next; - MEM_freeN(state); - } - MEM_freeN(range_pool->settings); - MEM_freeN(range_pool); -} - -typedef struct TaskParallelIteratorState { - void *userdata; - TaskParallelIteratorIterFunc iter_func; - TaskParallelIteratorFunc func; - - /* *** Data used to 'acquire' chunks of items from the iterator. *** */ - /* Common data also passed to the generator callback. */ - TaskParallelIteratorStateShared iter_shared; - /* Total number of items. If unknown, set it to a negative number. */ - int tot_items; -} TaskParallelIteratorState; - -BLI_INLINE void task_parallel_iterator_calc_chunk_size(const TaskParallelSettings *settings, - const int num_tasks, - TaskParallelIteratorState *state) -{ - task_parallel_calc_chunk_size( - settings, state->tot_items, num_tasks, &state->iter_shared.chunk_size); -} - -static void parallel_iterator_func_do(TaskParallelIteratorState *__restrict state, - void *userdata_chunk, - int threadid) -{ - TaskParallelTLS tls = { - .thread_id = threadid, - .userdata_chunk = userdata_chunk, - }; - - void **current_chunk_items; - int *current_chunk_indices; - int current_chunk_size; - - const size_t items_size = sizeof(*current_chunk_items) * (size_t)state->iter_shared.chunk_size; - const size_t indices_size = sizeof(*current_chunk_indices) * - (size_t)state->iter_shared.chunk_size; - - current_chunk_items = MALLOCA(items_size); - current_chunk_indices = MALLOCA(indices_size); - current_chunk_size = 0; - - for (bool do_abort = false; !do_abort;) { - if (state->iter_shared.spin_lock != NULL) { - BLI_spin_lock(state->iter_shared.spin_lock); - } - - /* Get current status. */ - int index = state->iter_shared.next_index; - void *item = state->iter_shared.next_item; - int i; - - /* 'Acquire' a chunk of items from the iterator function. */ - for (i = 0; i < state->iter_shared.chunk_size && !state->iter_shared.is_finished; i++) { - current_chunk_indices[i] = index; - current_chunk_items[i] = item; - state->iter_func(state->userdata, &tls, &item, &index, &state->iter_shared.is_finished); - } - - /* Update current status. */ - state->iter_shared.next_index = index; - state->iter_shared.next_item = item; - current_chunk_size = i; - - do_abort = state->iter_shared.is_finished; - - if (state->iter_shared.spin_lock != NULL) { - BLI_spin_unlock(state->iter_shared.spin_lock); - } - - for (i = 0; i < current_chunk_size; ++i) { - state->func(state->userdata, current_chunk_items[i], current_chunk_indices[i], &tls); - } - } - - MALLOCA_FREE(current_chunk_items, items_size); - MALLOCA_FREE(current_chunk_indices, indices_size); -} - -static void parallel_iterator_func(TaskPool *__restrict pool, void *userdata_chunk, int threadid) -{ - TaskParallelIteratorState *__restrict state = BLI_task_pool_userdata(pool); - - parallel_iterator_func_do(state, userdata_chunk, threadid); -} - -static void task_parallel_iterator_no_threads(const TaskParallelSettings *settings, - TaskParallelIteratorState *state) -{ - /* Prepare user's TLS data. */ - void *userdata_chunk = settings->userdata_chunk; - const size_t userdata_chunk_size = settings->userdata_chunk_size; - void *userdata_chunk_local = NULL; - const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); - if (use_userdata_chunk) { - userdata_chunk_local = MALLOCA(userdata_chunk_size); - memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); - } - - /* Also marking it as non-threaded for the iterator callback. */ - state->iter_shared.spin_lock = NULL; - - parallel_iterator_func_do(state, userdata_chunk, 0); - - if (use_userdata_chunk) { - if (settings->func_finalize != NULL) { - settings->func_finalize(state->userdata, userdata_chunk_local); - } - MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size); - } -} - -static void task_parallel_iterator_do(const TaskParallelSettings *settings, - TaskParallelIteratorState *state) -{ - TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); - - task_parallel_iterator_calc_chunk_size(settings, num_threads, state); - - if (!settings->use_threading) { - task_parallel_iterator_no_threads(settings, state); - return; - } - - const int chunk_size = state->iter_shared.chunk_size; - const int tot_items = state->tot_items; - const size_t num_tasks = tot_items >= 0 ? - (size_t)min_ii(num_threads, state->tot_items / chunk_size) : - (size_t)num_threads; - - BLI_assert(num_tasks > 0); - if (num_tasks == 1) { - task_parallel_iterator_no_threads(settings, state); - return; - } - - SpinLock spin_lock; - BLI_spin_init(&spin_lock); - state->iter_shared.spin_lock = &spin_lock; - - void *userdata_chunk = settings->userdata_chunk; - const size_t userdata_chunk_size = settings->userdata_chunk_size; - void *userdata_chunk_local = NULL; - void *userdata_chunk_array = NULL; - const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); - - TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, state); - - if (use_userdata_chunk) { - userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks); - } - - for (size_t i = 0; i < num_tasks; i++) { - if (use_userdata_chunk) { - userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); - memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); - } - /* Use this pool's pre-allocated tasks. */ - BLI_task_pool_push_from_thread(task_pool, - parallel_iterator_func, - userdata_chunk_local, - false, - TASK_PRIORITY_HIGH, - task_pool->thread_id); - } - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - - if (use_userdata_chunk) { - if (settings->func_finalize != NULL) { - for (size_t i = 0; i < num_tasks; i++) { - userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); - settings->func_finalize(state->userdata, userdata_chunk_local); - } - } - MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks); - } - - BLI_spin_end(&spin_lock); - state->iter_shared.spin_lock = NULL; -} - -/** - * This function allows to parallelize for loops using a generic iterator. - * - * \param userdata: Common userdata passed to all instances of \a func. - * \param iter_func: Callback function used to generate chunks of items. - * \param init_item: The initial item, if necessary (may be NULL if unused). - * \param init_index: The initial index. - * \param tot_items: The total amount of items to iterate over - * (if unknown, set it to a negative number). - * \param func: Callback function. - * \param settings: See public API doc of TaskParallelSettings for description of all settings. - * - * \note Static scheduling is only available when \a tot_items is >= 0. - */ - -void BLI_task_parallel_iterator(void *userdata, - TaskParallelIteratorIterFunc iter_func, - void *init_item, - const int init_index, - const int tot_items, - TaskParallelIteratorFunc func, - const TaskParallelSettings *settings) -{ - TaskParallelIteratorState state = {0}; - - state.tot_items = tot_items; - state.iter_shared.next_index = init_index; - state.iter_shared.next_item = init_item; - state.iter_shared.is_finished = false; - state.userdata = userdata; - state.iter_func = iter_func; - state.func = func; - - task_parallel_iterator_do(settings, &state); -} - -static void task_parallel_listbase_get(void *__restrict UNUSED(userdata), - const TaskParallelTLS *__restrict UNUSED(tls), - void **r_next_item, - int *r_next_index, - bool *r_do_abort) -{ - /* Get current status. */ - Link *link = *r_next_item; - - if (link->next == NULL) { - *r_do_abort = true; - } - *r_next_item = link->next; - (*r_next_index)++; -} - -/** - * This function allows to parallelize for loops over ListBase items. - * - * \param listbase: The double linked list to loop over. - * \param userdata: Common userdata passed to all instances of \a func. - * \param func: Callback function. - * \param settings: See public API doc of ParallelRangeSettings for description of all settings. - * - * \note There is no static scheduling here, - * since it would need another full loop over items to count them. - */ -void BLI_task_parallel_listbase(ListBase *listbase, - void *userdata, - TaskParallelIteratorFunc func, - const TaskParallelSettings *settings) -{ - if (BLI_listbase_is_empty(listbase)) { - return; - } - - TaskParallelIteratorState state = {0}; - - state.tot_items = BLI_listbase_count(listbase); - state.iter_shared.next_index = 0; - state.iter_shared.next_item = listbase->first; - state.iter_shared.is_finished = false; - state.userdata = userdata; - state.iter_func = task_parallel_listbase_get; - state.func = func; - - task_parallel_iterator_do(settings, &state); -} - -#undef MALLOCA -#undef MALLOCA_FREE - -typedef struct ParallelMempoolState { - void *userdata; - TaskParallelMempoolFunc func; -} ParallelMempoolState; - -static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata, int UNUSED(threadid)) -{ - ParallelMempoolState *__restrict state = BLI_task_pool_userdata(pool); - BLI_mempool_iter *iter = taskdata; - MempoolIterData *item; - - while ((item = BLI_mempool_iterstep(iter)) != NULL) { - state->func(state->userdata, item); - } -} - -/** - * This function allows to parallelize for loops over Mempool items. - * - * \param mempool: The iterable BLI_mempool to loop over. - * \param userdata: Common userdata passed to all instances of \a func. - * \param func: Callback function. - * \param use_threading: If \a true, actually split-execute loop in threads, - * else just do a sequential for loop - * (allows caller to use any kind of test to switch on parallelization or not). - * - * \note There is no static scheduling here. - */ -void BLI_task_parallel_mempool(BLI_mempool *mempool, - void *userdata, - TaskParallelMempoolFunc func, - const bool use_threading) -{ - TaskScheduler *task_scheduler; - TaskPool *task_pool; - ParallelMempoolState state; - int i, num_threads, num_tasks; - - if (BLI_mempool_len(mempool) == 0) { - return; - } - - if (!use_threading) { - BLI_mempool_iter iter; - BLI_mempool_iternew(mempool, &iter); - - for (void *item = BLI_mempool_iterstep(&iter); item != NULL; - item = BLI_mempool_iterstep(&iter)) { - func(userdata, item); - } - return; - } - - task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create_suspended(task_scheduler, &state); - num_threads = BLI_task_scheduler_num_threads(task_scheduler); - - /* The idea here is to prevent creating task for each of the loop iterations - * and instead have tasks which are evenly distributed across CPU cores and - * pull next item to be crunched using the threaded-aware BLI_mempool_iter. - */ - num_tasks = num_threads + 2; - - state.userdata = userdata; - state.func = func; - - BLI_mempool_iter *mempool_iterators = BLI_mempool_iter_threadsafe_create(mempool, - (size_t)num_tasks); - - for (i = 0; i < num_tasks; i++) { - /* Use this pool's pre-allocated tasks. */ - BLI_task_pool_push_from_thread(task_pool, - parallel_mempool_func, - &mempool_iterators[i], - false, - TASK_PRIORITY_HIGH, - task_pool->thread_id); - } - - BLI_task_pool_work_and_wait(task_pool); - BLI_task_pool_free(task_pool); - - BLI_mempool_iter_threadsafe_free(mempool_iterators); -} diff --git a/source/blender/blenlib/intern/task_iterator.c b/source/blender/blenlib/intern/task_iterator.c new file mode 100644 index 00000000000..4ac012fa578 --- /dev/null +++ b/source/blender/blenlib/intern/task_iterator.c @@ -0,0 +1,925 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + * + * A generic task system which can be used for any task based subsystem. + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_mempool.h" +#include "BLI_task.h" +#include "BLI_threads.h" + +#include "atomic_ops.h" + +/* Parallel range routines */ + +/** + * + * Main functions: + * - #BLI_task_parallel_range + * - #BLI_task_parallel_listbase (#ListBase - double linked list) + * + * TODO: + * - #BLI_task_parallel_foreach_link (#Link - single linked list) + * - #BLI_task_parallel_foreach_ghash/gset (#GHash/#GSet - hash & set) + * - #BLI_task_parallel_foreach_mempool (#BLI_mempool - iterate over mempools) + */ + +/* Allows to avoid using malloc for userdata_chunk in tasks, when small enough. */ +#define MALLOCA(_size) ((_size) <= 8192) ? alloca((_size)) : MEM_mallocN((_size), __func__) +#define MALLOCA_FREE(_mem, _size) \ + if (((_mem) != NULL) && ((_size) > 8192)) \ + MEM_freeN((_mem)) + +/* Stores all needed data to perform a parallelized iteration, + * with a same operation (callback function). + * It can be chained with other tasks in a single-linked list way. */ +typedef struct TaskParallelRangeState { + struct TaskParallelRangeState *next; + + /* Start and end point of integer value iteration. */ + int start, stop; + + /* User-defined data, shared between all worker threads. */ + void *userdata_shared; + /* User-defined callback function called for each value in [start, stop[ specified range. */ + TaskParallelRangeFunc func; + + /* Each instance of looping chunks will get a copy of this data + * (similar to OpenMP's firstprivate). + */ + void *initial_tls_memory; /* Pointer to actual user-defined 'tls' data. */ + size_t tls_data_size; /* Size of that data. */ + + void *flatten_tls_storage; /* 'tls' copies of initial_tls_memory for each running task. */ + /* Number of 'tls' copies in the array, i.e. number of worker threads. */ + size_t num_elements_in_tls_storage; + + /* Function called from calling thread once whole range have been processed. */ + TaskParallelFinalizeFunc func_finalize; + + /* Current value of the iterator, shared between all threads (atomically updated). */ + int iter_value; + int iter_chunk_num; /* Amount of iterations to process in a single step. */ +} TaskParallelRangeState; + +/* Stores all the parallel tasks for a single pool. */ +typedef struct TaskParallelRangePool { + /* The workers' task pool. */ + TaskPool *pool; + /* The number of worker tasks we need to create. */ + int num_tasks; + /* The total number of iterations in all the added ranges. */ + int num_total_iters; + /* The size (number of items) processed at once by a worker task. */ + int chunk_size; + + /* Linked list of range tasks to process. */ + TaskParallelRangeState *parallel_range_states; + /* Current range task beeing processed, swapped atomically. */ + TaskParallelRangeState *current_state; + /* Scheduling settings common to all tasks. */ + TaskParallelSettings *settings; +} TaskParallelRangePool; + +BLI_INLINE void task_parallel_calc_chunk_size(const TaskParallelSettings *settings, + const int tot_items, + int num_tasks, + int *r_chunk_size) +{ + int chunk_size = 0; + + if (!settings->use_threading) { + /* Some users of this helper will still need a valid chunk size in case processing is not + * threaded. We can use a bigger one than in default threaded case then. */ + chunk_size = 1024; + num_tasks = 1; + } + else if (settings->min_iter_per_thread > 0) { + /* Already set by user, no need to do anything here. */ + chunk_size = settings->min_iter_per_thread; + } + else { + /* Multiplier used in heuristics below to define "optimal" chunk size. + * The idea here is to increase the chunk size to compensate for a rather measurable threading + * overhead caused by fetching tasks. With too many CPU threads we are starting + * to spend too much time in those overheads. + * First values are: 1 if num_tasks < 16; + * else 2 if num_tasks < 32; + * else 3 if num_tasks < 48; + * else 4 if num_tasks < 64; + * etc. + * Note: If we wanted to keep the 'power of two' multiplier, we'd need something like: + * 1 << max_ii(0, (int)(sizeof(int) * 8) - 1 - bitscan_reverse_i(num_tasks) - 3) + */ + const int num_tasks_factor = max_ii(1, num_tasks >> 3); + + /* We could make that 'base' 32 number configurable in TaskParallelSettings too, or maybe just + * always use that heuristic using TaskParallelSettings.min_iter_per_thread as basis? */ + chunk_size = 32 * num_tasks_factor; + + /* Basic heuristic to avoid threading on low amount of items. + * We could make that limit configurable in settings too. */ + if (tot_items > 0 && tot_items < max_ii(256, chunk_size * 2)) { + chunk_size = tot_items; + } + } + + BLI_assert(chunk_size > 0); + + if (tot_items > 0) { + switch (settings->scheduling_mode) { + case TASK_SCHEDULING_STATIC: + *r_chunk_size = max_ii(chunk_size, tot_items / num_tasks); + break; + case TASK_SCHEDULING_DYNAMIC: + *r_chunk_size = chunk_size; + break; + } + } + else { + /* If total amount of items is unknown, we can only use dynamic scheduling. */ + *r_chunk_size = chunk_size; + } +} + +BLI_INLINE void task_parallel_range_calc_chunk_size(TaskParallelRangePool *range_pool) +{ + int num_iters = 0; + int min_num_iters = INT_MAX; + for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; + state = state->next) { + const int ni = state->stop - state->start; + num_iters += ni; + if (min_num_iters > ni) { + min_num_iters = ni; + } + } + range_pool->num_total_iters = num_iters; + /* Note: Passing min_num_iters here instead of num_iters kind of partially breaks the 'static' + * scheduling, but pooled range iterator is inherently non-static anyway, so adding a small level + * of dynamic scheduling here should be fine. */ + task_parallel_calc_chunk_size( + range_pool->settings, min_num_iters, range_pool->num_tasks, &range_pool->chunk_size); +} + +BLI_INLINE bool parallel_range_next_iter_get(TaskParallelRangePool *__restrict range_pool, + int *__restrict r_iter, + int *__restrict r_count, + TaskParallelRangeState **__restrict r_state) +{ + /* We need an atomic op here as well to fetch the initial state, since some other thread might + * have already updated it. */ + TaskParallelRangeState *current_state = atomic_cas_ptr( + (void **)&range_pool->current_state, NULL, NULL); + + int previter = INT32_MAX; + + while (current_state != NULL && previter >= current_state->stop) { + previter = atomic_fetch_and_add_int32(¤t_state->iter_value, range_pool->chunk_size); + *r_iter = previter; + *r_count = max_ii(0, min_ii(range_pool->chunk_size, current_state->stop - previter)); + + if (previter >= current_state->stop) { + /* At this point the state we got is done, we need to go to the next one. In case some other + * thread already did it, then this does nothing, and we'll just get current valid state + * at start of the next loop. */ + TaskParallelRangeState *current_state_from_atomic_cas = atomic_cas_ptr( + (void **)&range_pool->current_state, current_state, current_state->next); + + if (current_state == current_state_from_atomic_cas) { + /* The atomic CAS operation was successful, we did update range_pool->current_state, so we + * can safely switch to next state. */ + current_state = current_state->next; + } + else { + /* The atomic CAS operation failed, but we still got range_pool->current_state value out of + * it, just use it as our new current state. */ + current_state = current_state_from_atomic_cas; + } + } + } + + *r_state = current_state; + return (current_state != NULL && previter < current_state->stop); +} + +static void parallel_range_func(TaskPool *__restrict pool, void *tls_data_idx, int thread_id) +{ + TaskParallelRangePool *__restrict range_pool = BLI_task_pool_userdata(pool); + TaskParallelTLS tls = { + .thread_id = thread_id, + .userdata_chunk = NULL, + }; + TaskParallelRangeState *state; + int iter, count; + while (parallel_range_next_iter_get(range_pool, &iter, &count, &state)) { + tls.userdata_chunk = (char *)state->flatten_tls_storage + + (((size_t)POINTER_AS_INT(tls_data_idx)) * state->tls_data_size); + for (int i = 0; i < count; i++) { + state->func(state->userdata_shared, iter + i, &tls); + } + } +} + +static void parallel_range_single_thread(TaskParallelRangePool *range_pool) +{ + for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; + state = state->next) { + const int start = state->start; + const int stop = state->stop; + void *userdata = state->userdata_shared; + TaskParallelRangeFunc func = state->func; + + void *initial_tls_memory = state->initial_tls_memory; + const size_t tls_data_size = state->tls_data_size; + void *flatten_tls_storage = NULL; + const bool use_tls_data = (tls_data_size != 0) && (initial_tls_memory != NULL); + if (use_tls_data) { + flatten_tls_storage = MALLOCA(tls_data_size); + memcpy(flatten_tls_storage, initial_tls_memory, tls_data_size); + } + TaskParallelTLS tls = { + .thread_id = 0, + .userdata_chunk = flatten_tls_storage, + }; + for (int i = start; i < stop; i++) { + func(userdata, i, &tls); + } + if (state->func_finalize != NULL) { + state->func_finalize(userdata, flatten_tls_storage); + } + MALLOCA_FREE(flatten_tls_storage, tls_data_size); + } +} + +/** + * This function allows to parallelized for loops in a similar way to OpenMP's + * 'parallel for' statement. + * + * See public API doc of ParallelRangeSettings for description of all settings. + */ +void BLI_task_parallel_range(const int start, + const int stop, + void *userdata, + TaskParallelRangeFunc func, + TaskParallelSettings *settings) +{ + if (start == stop) { + return; + } + + BLI_assert(start < stop); + + TaskParallelRangeState state = { + .next = NULL, + .start = start, + .stop = stop, + .userdata_shared = userdata, + .func = func, + .iter_value = start, + .initial_tls_memory = settings->userdata_chunk, + .tls_data_size = settings->userdata_chunk_size, + .func_finalize = settings->func_finalize, + }; + TaskParallelRangePool range_pool = { + .pool = NULL, .parallel_range_states = &state, .current_state = NULL, .settings = settings}; + int i, num_threads, num_tasks; + + void *tls_data = settings->userdata_chunk; + const size_t tls_data_size = settings->userdata_chunk_size; + if (tls_data_size != 0) { + BLI_assert(tls_data != NULL); + } + const bool use_tls_data = (tls_data_size != 0) && (tls_data != NULL); + void *flatten_tls_storage = NULL; + + /* If it's not enough data to be crunched, don't bother with tasks at all, + * do everything from the current thread. + */ + if (!settings->use_threading) { + parallel_range_single_thread(&range_pool); + return; + } + + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + num_threads = BLI_task_scheduler_num_threads(task_scheduler); + + /* The idea here is to prevent creating task for each of the loop iterations + * and instead have tasks which are evenly distributed across CPU cores and + * pull next iter to be crunched using the queue. + */ + range_pool.num_tasks = num_tasks = num_threads + 2; + + task_parallel_range_calc_chunk_size(&range_pool); + range_pool.num_tasks = num_tasks = min_ii(num_tasks, + max_ii(1, (stop - start) / range_pool.chunk_size)); + + if (num_tasks == 1) { + parallel_range_single_thread(&range_pool); + return; + } + + TaskPool *task_pool = range_pool.pool = BLI_task_pool_create_suspended( + task_scheduler, &range_pool, TASK_PRIORITY_HIGH); + + range_pool.current_state = &state; + + if (use_tls_data) { + state.flatten_tls_storage = flatten_tls_storage = MALLOCA(tls_data_size * (size_t)num_tasks); + state.tls_data_size = tls_data_size; + } + + const int thread_id = BLI_task_pool_creator_thread_id(task_pool); + for (i = 0; i < num_tasks; i++) { + if (use_tls_data) { + void *userdata_chunk_local = (char *)flatten_tls_storage + (tls_data_size * (size_t)i); + memcpy(userdata_chunk_local, tls_data, tls_data_size); + } + /* Use this pool's pre-allocated tasks. */ + BLI_task_pool_push_from_thread( + task_pool, parallel_range_func, POINTER_FROM_INT(i), false, NULL, thread_id); + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + if (use_tls_data) { + if (settings->func_finalize != NULL) { + for (i = 0; i < num_tasks; i++) { + void *userdata_chunk_local = (char *)flatten_tls_storage + (tls_data_size * (size_t)i); + settings->func_finalize(userdata, userdata_chunk_local); + } + } + MALLOCA_FREE(flatten_tls_storage, tls_data_size * (size_t)num_tasks); + } +} + +/** + * Initialize a task pool to parallelize several for loops at the same time. + * + * See public API doc of ParallelRangeSettings for description of all settings. + * Note that loop-specific settings (like 'tls' data or finalize function) must be left NULL here. + * Only settings controlling how iteration is parallelized must be defined, as those will affect + * all loops added to that pool. + */ +TaskParallelRangePool *BLI_task_parallel_range_pool_init(const TaskParallelSettings *settings) +{ + TaskParallelRangePool *range_pool = MEM_callocN(sizeof(*range_pool), __func__); + + BLI_assert(settings->userdata_chunk == NULL); + BLI_assert(settings->func_finalize == NULL); + range_pool->settings = MEM_mallocN(sizeof(*range_pool->settings), __func__); + *range_pool->settings = *settings; + + return range_pool; +} + +/** + * Add a loop task to the pool. It does not execute it at all. + * + * See public API doc of ParallelRangeSettings for description of all settings. + * Note that only 'tls'-related data are used here. + */ +void BLI_task_parallel_range_pool_push(TaskParallelRangePool *range_pool, + const int start, + const int stop, + void *userdata, + TaskParallelRangeFunc func, + const TaskParallelSettings *settings) +{ + BLI_assert(range_pool->pool == NULL); + + if (start == stop) { + return; + } + + BLI_assert(start < stop); + if (settings->userdata_chunk_size != 0) { + BLI_assert(settings->userdata_chunk != NULL); + } + + TaskParallelRangeState *state = MEM_callocN(sizeof(*state), __func__); + state->start = start; + state->stop = stop; + state->userdata_shared = userdata; + state->func = func; + state->iter_value = start; + state->initial_tls_memory = settings->userdata_chunk; + state->tls_data_size = settings->userdata_chunk_size; + state->func_finalize = settings->func_finalize; + + state->next = range_pool->parallel_range_states; + range_pool->parallel_range_states = state; +} + +static void parallel_range_func_finalize(TaskPool *__restrict pool, + void *v_state, + int UNUSED(thread_id)) +{ + TaskParallelRangePool *__restrict range_pool = BLI_task_pool_userdata(pool); + TaskParallelRangeState *state = v_state; + + for (int i = 0; i < range_pool->num_tasks; i++) { + void *tls_data = (char *)state->flatten_tls_storage + (state->tls_data_size * (size_t)i); + state->func_finalize(state->userdata_shared, tls_data); + } +} + +/** + * Run all tasks pushed to the range_pool. + * + * Note that the range pool is re-usable (you may push new tasks into it and call this function + * again). + */ +void BLI_task_parallel_range_pool_work_and_wait(TaskParallelRangePool *range_pool) +{ + BLI_assert(range_pool->pool == NULL); + + /* If it's not enough data to be crunched, don't bother with tasks at all, + * do everything from the current thread. + */ + if (!range_pool->settings->use_threading) { + parallel_range_single_thread(range_pool); + return; + } + + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); + + /* The idea here is to prevent creating task for each of the loop iterations + * and instead have tasks which are evenly distributed across CPU cores and + * pull next iter to be crunched using the queue. + */ + int num_tasks = num_threads + 2; + range_pool->num_tasks = num_tasks; + + task_parallel_range_calc_chunk_size(range_pool); + range_pool->num_tasks = num_tasks = min_ii( + num_tasks, max_ii(1, range_pool->num_total_iters / range_pool->chunk_size)); + + if (num_tasks == 1) { + parallel_range_single_thread(range_pool); + return; + } + + /* We create all 'tls' data here in a single loop. */ + for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; + state = state->next) { + void *userdata_chunk = state->initial_tls_memory; + const size_t userdata_chunk_size = state->tls_data_size; + if (userdata_chunk_size == 0) { + BLI_assert(userdata_chunk == NULL); + continue; + } + + void *userdata_chunk_array = NULL; + state->flatten_tls_storage = userdata_chunk_array = MALLOCA(userdata_chunk_size * + (size_t)num_tasks); + for (int i = 0; i < num_tasks; i++) { + void *userdata_chunk_local = (char *)userdata_chunk_array + + (userdata_chunk_size * (size_t)i); + memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + } + } + + TaskPool *task_pool = range_pool->pool = BLI_task_pool_create_suspended( + task_scheduler, range_pool, TASK_PRIORITY_HIGH); + + range_pool->current_state = range_pool->parallel_range_states; + const int thread_id = BLI_task_pool_creator_thread_id(task_pool); + for (int i = 0; i < num_tasks; i++) { + BLI_task_pool_push_from_thread( + task_pool, parallel_range_func, POINTER_FROM_INT(i), false, NULL, thread_id); + } + + BLI_task_pool_work_and_wait(task_pool); + + BLI_assert(atomic_cas_ptr((void **)&range_pool->current_state, NULL, NULL) == NULL); + + /* Finalize all tasks. */ + for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; + state = state->next) { + const size_t userdata_chunk_size = state->tls_data_size; + void *userdata_chunk_array = state->flatten_tls_storage; + UNUSED_VARS_NDEBUG(userdata_chunk_array); + if (userdata_chunk_size == 0) { + BLI_assert(userdata_chunk_array == NULL); + continue; + } + + if (state->func_finalize != NULL) { + BLI_task_pool_push_from_thread( + task_pool, parallel_range_func_finalize, state, false, NULL, thread_id); + } + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + range_pool->pool = NULL; + + /* Cleanup all tasks. */ + TaskParallelRangeState *state_next; + for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; + state = state_next) { + state_next = state->next; + + const size_t userdata_chunk_size = state->tls_data_size; + void *userdata_chunk_array = state->flatten_tls_storage; + if (userdata_chunk_size != 0) { + BLI_assert(userdata_chunk_array != NULL); + MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * (size_t)num_tasks); + } + + MEM_freeN(state); + } + range_pool->parallel_range_states = NULL; +} + +/** + * Clear/free given \a range_pool. + */ +void BLI_task_parallel_range_pool_free(TaskParallelRangePool *range_pool) +{ + TaskParallelRangeState *state_next = NULL; + for (TaskParallelRangeState *state = range_pool->parallel_range_states; state != NULL; + state = state_next) { + state_next = state->next; + MEM_freeN(state); + } + MEM_freeN(range_pool->settings); + MEM_freeN(range_pool); +} + +typedef struct TaskParallelIteratorState { + void *userdata; + TaskParallelIteratorIterFunc iter_func; + TaskParallelIteratorFunc func; + + /* *** Data used to 'acquire' chunks of items from the iterator. *** */ + /* Common data also passed to the generator callback. */ + TaskParallelIteratorStateShared iter_shared; + /* Total number of items. If unknown, set it to a negative number. */ + int tot_items; +} TaskParallelIteratorState; + +BLI_INLINE void task_parallel_iterator_calc_chunk_size(const TaskParallelSettings *settings, + const int num_tasks, + TaskParallelIteratorState *state) +{ + task_parallel_calc_chunk_size( + settings, state->tot_items, num_tasks, &state->iter_shared.chunk_size); +} + +static void parallel_iterator_func_do(TaskParallelIteratorState *__restrict state, + void *userdata_chunk, + int threadid) +{ + TaskParallelTLS tls = { + .thread_id = threadid, + .userdata_chunk = userdata_chunk, + }; + + void **current_chunk_items; + int *current_chunk_indices; + int current_chunk_size; + + const size_t items_size = sizeof(*current_chunk_items) * (size_t)state->iter_shared.chunk_size; + const size_t indices_size = sizeof(*current_chunk_indices) * + (size_t)state->iter_shared.chunk_size; + + current_chunk_items = MALLOCA(items_size); + current_chunk_indices = MALLOCA(indices_size); + current_chunk_size = 0; + + for (bool do_abort = false; !do_abort;) { + if (state->iter_shared.spin_lock != NULL) { + BLI_spin_lock(state->iter_shared.spin_lock); + } + + /* Get current status. */ + int index = state->iter_shared.next_index; + void *item = state->iter_shared.next_item; + int i; + + /* 'Acquire' a chunk of items from the iterator function. */ + for (i = 0; i < state->iter_shared.chunk_size && !state->iter_shared.is_finished; i++) { + current_chunk_indices[i] = index; + current_chunk_items[i] = item; + state->iter_func(state->userdata, &tls, &item, &index, &state->iter_shared.is_finished); + } + + /* Update current status. */ + state->iter_shared.next_index = index; + state->iter_shared.next_item = item; + current_chunk_size = i; + + do_abort = state->iter_shared.is_finished; + + if (state->iter_shared.spin_lock != NULL) { + BLI_spin_unlock(state->iter_shared.spin_lock); + } + + for (i = 0; i < current_chunk_size; ++i) { + state->func(state->userdata, current_chunk_items[i], current_chunk_indices[i], &tls); + } + } + + MALLOCA_FREE(current_chunk_items, items_size); + MALLOCA_FREE(current_chunk_indices, indices_size); +} + +static void parallel_iterator_func(TaskPool *__restrict pool, void *userdata_chunk, int threadid) +{ + TaskParallelIteratorState *__restrict state = BLI_task_pool_userdata(pool); + + parallel_iterator_func_do(state, userdata_chunk, threadid); +} + +static void task_parallel_iterator_no_threads(const TaskParallelSettings *settings, + TaskParallelIteratorState *state) +{ + /* Prepare user's TLS data. */ + void *userdata_chunk = settings->userdata_chunk; + const size_t userdata_chunk_size = settings->userdata_chunk_size; + void *userdata_chunk_local = NULL; + const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); + if (use_userdata_chunk) { + userdata_chunk_local = MALLOCA(userdata_chunk_size); + memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + } + + /* Also marking it as non-threaded for the iterator callback. */ + state->iter_shared.spin_lock = NULL; + + parallel_iterator_func_do(state, userdata_chunk, 0); + + if (use_userdata_chunk) { + if (settings->func_finalize != NULL) { + settings->func_finalize(state->userdata, userdata_chunk_local); + } + MALLOCA_FREE(userdata_chunk_local, userdata_chunk_size); + } +} + +static void task_parallel_iterator_do(const TaskParallelSettings *settings, + TaskParallelIteratorState *state) +{ + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + const int num_threads = BLI_task_scheduler_num_threads(task_scheduler); + + task_parallel_iterator_calc_chunk_size(settings, num_threads, state); + + if (!settings->use_threading) { + task_parallel_iterator_no_threads(settings, state); + return; + } + + const int chunk_size = state->iter_shared.chunk_size; + const int tot_items = state->tot_items; + const size_t num_tasks = tot_items >= 0 ? + (size_t)min_ii(num_threads, state->tot_items / chunk_size) : + (size_t)num_threads; + + BLI_assert(num_tasks > 0); + if (num_tasks == 1) { + task_parallel_iterator_no_threads(settings, state); + return; + } + + SpinLock spin_lock; + BLI_spin_init(&spin_lock); + state->iter_shared.spin_lock = &spin_lock; + + void *userdata_chunk = settings->userdata_chunk; + const size_t userdata_chunk_size = settings->userdata_chunk_size; + void *userdata_chunk_local = NULL; + void *userdata_chunk_array = NULL; + const bool use_userdata_chunk = (userdata_chunk_size != 0) && (userdata_chunk != NULL); + + TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, state, TASK_PRIORITY_HIGH); + + if (use_userdata_chunk) { + userdata_chunk_array = MALLOCA(userdata_chunk_size * num_tasks); + } + + const int thread_id = BLI_task_pool_creator_thread_id(task_pool); + for (size_t i = 0; i < num_tasks; i++) { + if (use_userdata_chunk) { + userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); + memcpy(userdata_chunk_local, userdata_chunk, userdata_chunk_size); + } + /* Use this pool's pre-allocated tasks. */ + BLI_task_pool_push_from_thread( + task_pool, parallel_iterator_func, userdata_chunk_local, false, NULL, thread_id); + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + if (use_userdata_chunk) { + if (settings->func_finalize != NULL) { + for (size_t i = 0; i < num_tasks; i++) { + userdata_chunk_local = (char *)userdata_chunk_array + (userdata_chunk_size * i); + settings->func_finalize(state->userdata, userdata_chunk_local); + } + } + MALLOCA_FREE(userdata_chunk_array, userdata_chunk_size * num_tasks); + } + + BLI_spin_end(&spin_lock); + state->iter_shared.spin_lock = NULL; +} + +/** + * This function allows to parallelize for loops using a generic iterator. + * + * \param userdata: Common userdata passed to all instances of \a func. + * \param iter_func: Callback function used to generate chunks of items. + * \param init_item: The initial item, if necessary (may be NULL if unused). + * \param init_index: The initial index. + * \param tot_items: The total amount of items to iterate over + * (if unknown, set it to a negative number). + * \param func: Callback function. + * \param settings: See public API doc of TaskParallelSettings for description of all settings. + * + * \note Static scheduling is only available when \a tot_items is >= 0. + */ + +void BLI_task_parallel_iterator(void *userdata, + TaskParallelIteratorIterFunc iter_func, + void *init_item, + const int init_index, + const int tot_items, + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings) +{ + TaskParallelIteratorState state = {0}; + + state.tot_items = tot_items; + state.iter_shared.next_index = init_index; + state.iter_shared.next_item = init_item; + state.iter_shared.is_finished = false; + state.userdata = userdata; + state.iter_func = iter_func; + state.func = func; + + task_parallel_iterator_do(settings, &state); +} + +static void task_parallel_listbase_get(void *__restrict UNUSED(userdata), + const TaskParallelTLS *__restrict UNUSED(tls), + void **r_next_item, + int *r_next_index, + bool *r_do_abort) +{ + /* Get current status. */ + Link *link = *r_next_item; + + if (link->next == NULL) { + *r_do_abort = true; + } + *r_next_item = link->next; + (*r_next_index)++; +} + +/** + * This function allows to parallelize for loops over ListBase items. + * + * \param listbase: The double linked list to loop over. + * \param userdata: Common userdata passed to all instances of \a func. + * \param func: Callback function. + * \param settings: See public API doc of ParallelRangeSettings for description of all settings. + * + * \note There is no static scheduling here, + * since it would need another full loop over items to count them. + */ +void BLI_task_parallel_listbase(ListBase *listbase, + void *userdata, + TaskParallelIteratorFunc func, + const TaskParallelSettings *settings) +{ + if (BLI_listbase_is_empty(listbase)) { + return; + } + + TaskParallelIteratorState state = {0}; + + state.tot_items = BLI_listbase_count(listbase); + state.iter_shared.next_index = 0; + state.iter_shared.next_item = listbase->first; + state.iter_shared.is_finished = false; + state.userdata = userdata; + state.iter_func = task_parallel_listbase_get; + state.func = func; + + task_parallel_iterator_do(settings, &state); +} + +#undef MALLOCA +#undef MALLOCA_FREE + +typedef struct ParallelMempoolState { + void *userdata; + TaskParallelMempoolFunc func; +} ParallelMempoolState; + +static void parallel_mempool_func(TaskPool *__restrict pool, void *taskdata, int UNUSED(threadid)) +{ + ParallelMempoolState *__restrict state = BLI_task_pool_userdata(pool); + BLI_mempool_iter *iter = taskdata; + MempoolIterData *item; + + while ((item = BLI_mempool_iterstep(iter)) != NULL) { + state->func(state->userdata, item); + } +} + +/** + * This function allows to parallelize for loops over Mempool items. + * + * \param mempool: The iterable BLI_mempool to loop over. + * \param userdata: Common userdata passed to all instances of \a func. + * \param func: Callback function. + * \param use_threading: If \a true, actually split-execute loop in threads, + * else just do a sequential for loop + * (allows caller to use any kind of test to switch on parallelization or not). + * + * \note There is no static scheduling here. + */ +void BLI_task_parallel_mempool(BLI_mempool *mempool, + void *userdata, + TaskParallelMempoolFunc func, + const bool use_threading) +{ + TaskScheduler *task_scheduler; + TaskPool *task_pool; + ParallelMempoolState state; + int i, num_threads, num_tasks; + + if (BLI_mempool_len(mempool) == 0) { + return; + } + + if (!use_threading) { + BLI_mempool_iter iter; + BLI_mempool_iternew(mempool, &iter); + + for (void *item = BLI_mempool_iterstep(&iter); item != NULL; + item = BLI_mempool_iterstep(&iter)) { + func(userdata, item); + } + return; + } + + task_scheduler = BLI_task_scheduler_get(); + task_pool = BLI_task_pool_create_suspended(task_scheduler, &state, TASK_PRIORITY_HIGH); + num_threads = BLI_task_scheduler_num_threads(task_scheduler); + + /* The idea here is to prevent creating task for each of the loop iterations + * and instead have tasks which are evenly distributed across CPU cores and + * pull next item to be crunched using the threaded-aware BLI_mempool_iter. + */ + num_tasks = num_threads + 2; + + state.userdata = userdata; + state.func = func; + + BLI_mempool_iter *mempool_iterators = BLI_mempool_iter_threadsafe_create(mempool, + (size_t)num_tasks); + + const int thread_id = BLI_task_pool_creator_thread_id(task_pool); + for (i = 0; i < num_tasks; i++) { + /* Use this pool's pre-allocated tasks. */ + BLI_task_pool_push_from_thread( + task_pool, parallel_mempool_func, &mempool_iterators[i], false, NULL, thread_id); + } + + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); + + BLI_mempool_iter_threadsafe_free(mempool_iterators); +} diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc new file mode 100644 index 00000000000..8085d495248 --- /dev/null +++ b/source/blender/blenlib/intern/task_pool.cc @@ -0,0 +1,1029 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + * + * A generic task system which can be used for any task based subsystem. + */ + +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_mempool.h" +#include "BLI_task.h" +#include "BLI_threads.h" + +#include "atomic_ops.h" + +/* Define this to enable some detailed statistic print. */ +#undef DEBUG_STATS + +/* Types */ + +/* Number of per-thread pre-allocated tasks. + * + * For more details see description of TaskMemPool. + */ +#define MEMPOOL_SIZE 256 + +/* Number of tasks which are pushed directly to local thread queue. + * + * This allows thread to fetch next task without locking the whole queue. + */ +#define LOCAL_QUEUE_SIZE 1 + +/* Number of tasks which are allowed to be scheduled in a delayed manner. + * + * This allows to use less locks per graph node children schedule. More details + * could be found at TaskThreadLocalStorage::do_delayed_push. + */ +#define DELAYED_QUEUE_SIZE 4096 + +#ifndef NDEBUG +# define ASSERT_THREAD_ID(scheduler, thread_id) \ + do { \ + if (!BLI_thread_is_main()) { \ + TaskThread *thread = (TaskThread *)pthread_getspecific(scheduler->tls_id_key); \ + if (thread == NULL) { \ + BLI_assert(thread_id == 0); \ + } \ + else { \ + BLI_assert(thread_id == thread->id); \ + } \ + } \ + else { \ + BLI_assert(thread_id == 0); \ + } \ + } while (false) +#else +# define ASSERT_THREAD_ID(scheduler, thread_id) +#endif + +typedef struct Task { + struct Task *next, *prev; + + TaskRunFunction run; + void *taskdata; + bool free_taskdata; + TaskFreeFunction freedata; + TaskPool *pool; +} Task; + +/* This is a per-thread storage of pre-allocated tasks. + * + * The idea behind this is simple: reduce amount of malloc() calls when pushing + * new task to the pool. This is done by keeping memory from the tasks which + * were finished already, so instead of freeing that memory we put it to the + * pool for the later re-use. + * + * The tricky part here is to avoid any inter-thread synchronization, hence no + * lock must exist around this pool. The pool will become an owner of the pointer + * from freed task, and only corresponding thread will be able to use this pool + * (no memory stealing and such). + * + * This leads to the following use of the pool: + * + * - task_push() should provide proper thread ID from which the task is being + * pushed from. + * + * - Task allocation function which check corresponding memory pool and if there + * is any memory in there it'll mark memory as re-used, remove it from the pool + * and use that memory for the new task. + * + * At this moment task queue owns the memory. + * + * - When task is done and task_free() is called the memory will be put to the + * pool which corresponds to a thread which handled the task. + */ +typedef struct TaskMemPool { + /* Number of pre-allocated tasks in the pool. */ + int num_tasks; + /* Pre-allocated task memory pointers. */ + Task *tasks[MEMPOOL_SIZE]; +} TaskMemPool; + +#ifdef DEBUG_STATS +typedef struct TaskMemPoolStats { + /* Number of allocations. */ + int num_alloc; + /* Number of avoided allocations (pointer was re-used from the pool). */ + int num_reuse; + /* Number of discarded memory due to pool saturation, */ + int num_discard; +} TaskMemPoolStats; +#endif + +typedef struct TaskThreadLocalStorage { + /* Memory pool for faster task allocation. + * The idea is to re-use memory of finished/discarded tasks by this thread. + */ + TaskMemPool task_mempool; + + /* Local queue keeps thread alive by keeping small amount of tasks ready + * to be picked up without causing global thread locks for synchronization. + */ + int num_local_queue; + Task *local_queue[LOCAL_QUEUE_SIZE]; + + /* Thread can be marked for delayed tasks push. This is helpful when it's + * know that lots of subsequent task pushed will happen from the same thread + * without "interrupting" for task execution. + * + * We try to accumulate as much tasks as possible in a local queue without + * any locks first, and then we push all of them into a scheduler's queue + * from within a single mutex lock. + */ + bool do_delayed_push; + int num_delayed_queue; + Task *delayed_queue[DELAYED_QUEUE_SIZE]; +} TaskThreadLocalStorage; + +struct TaskPool { + TaskScheduler *scheduler; + + volatile size_t num; + ThreadMutex num_mutex; + ThreadCondition num_cond; + + void *userdata; + ThreadMutex user_mutex; + + volatile bool do_cancel; + volatile bool do_work; + + volatile bool is_suspended; + bool start_suspended; + ListBase suspended_queue; + size_t num_suspended; + + TaskPriority priority; + + /* If set, this pool may never be work_and_wait'ed, which means TaskScheduler + * has to use its special background fallback thread in case we are in + * single-threaded situation. + */ + bool run_in_background; + + /* This is a task scheduler's ID of a thread at which pool was constructed. + * It will be used to access task TLS. + */ + int thread_id; + + /* For the pools which are created from non-main thread which is not a + * scheduler worker thread we can't re-use any of scheduler's threads TLS + * and have to use our own one. + */ + bool use_local_tls; + TaskThreadLocalStorage local_tls; +#ifndef NDEBUG + pthread_t creator_thread_id; +#endif + +#ifdef DEBUG_STATS + TaskMemPoolStats *mempool_stats; +#endif +}; + +struct TaskScheduler { + pthread_t *threads; + struct TaskThread *task_threads; + int num_threads; + bool background_thread_only; + + ListBase queue; + ThreadMutex queue_mutex; + ThreadCondition queue_cond; + + ThreadMutex startup_mutex; + ThreadCondition startup_cond; + volatile int num_thread_started; + + volatile bool do_exit; + + /* NOTE: In pthread's TLS we store the whole TaskThread structure. */ + pthread_key_t tls_id_key; +}; + +typedef struct TaskThread { + TaskScheduler *scheduler; + int id; + TaskThreadLocalStorage tls; +} TaskThread; + +/* Helper */ +BLI_INLINE void task_data_free(Task *task, const int thread_id) +{ + if (task->free_taskdata) { + if (task->freedata) { + task->freedata(task->pool, task->taskdata, thread_id); + } + else { + MEM_freeN(task->taskdata); + } + } +} + +BLI_INLINE void initialize_task_tls(TaskThreadLocalStorage *tls) +{ + memset(tls, 0, sizeof(TaskThreadLocalStorage)); +} + +BLI_INLINE TaskThreadLocalStorage *get_task_tls(TaskPool *pool, const int thread_id) +{ + TaskScheduler *scheduler = pool->scheduler; + BLI_assert(thread_id >= 0); + BLI_assert(thread_id <= scheduler->num_threads); + if (pool->use_local_tls && thread_id == 0) { + BLI_assert(pool->thread_id == 0); + BLI_assert(!BLI_thread_is_main()); + BLI_assert(pthread_equal(pthread_self(), pool->creator_thread_id)); + return &pool->local_tls; + } + if (thread_id == 0) { + BLI_assert(BLI_thread_is_main()); + return &scheduler->task_threads[pool->thread_id].tls; + } + return &scheduler->task_threads[thread_id].tls; +} + +BLI_INLINE void free_task_tls(TaskThreadLocalStorage *tls) +{ + TaskMemPool *task_mempool = &tls->task_mempool; + for (int i = 0; i < task_mempool->num_tasks; i++) { + MEM_freeN(task_mempool->tasks[i]); + } +} + +static Task *task_alloc(TaskPool *pool, const int thread_id) +{ + BLI_assert(thread_id <= pool->scheduler->num_threads); + if (thread_id != -1) { + BLI_assert(thread_id >= 0); + BLI_assert(thread_id <= pool->scheduler->num_threads); + TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); + TaskMemPool *task_mempool = &tls->task_mempool; + /* Try to re-use task memory from a thread local storage. */ + if (task_mempool->num_tasks > 0) { + --task_mempool->num_tasks; + /* Success! We've just avoided task allocation. */ +#ifdef DEBUG_STATS + pool->mempool_stats[thread_id].num_reuse++; +#endif + return task_mempool->tasks[task_mempool->num_tasks]; + } + /* We are doomed to allocate new task data. */ +#ifdef DEBUG_STATS + pool->mempool_stats[thread_id].num_alloc++; +#endif + } + return (Task *)MEM_mallocN(sizeof(Task), "New task"); +} + +static void task_free(TaskPool *pool, Task *task, const int thread_id) +{ + task_data_free(task, thread_id); + BLI_assert(thread_id >= 0); + BLI_assert(thread_id <= pool->scheduler->num_threads); + if (thread_id == 0) { + BLI_assert(pool->use_local_tls || BLI_thread_is_main()); + } + TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); + TaskMemPool *task_mempool = &tls->task_mempool; + if (task_mempool->num_tasks < MEMPOOL_SIZE - 1) { + /* Successfully allowed the task to be re-used later. */ + task_mempool->tasks[task_mempool->num_tasks] = task; + ++task_mempool->num_tasks; + } + else { + /* Local storage saturated, no other way than just discard + * the memory. + * + * TODO(sergey): We can perhaps store such pointer in a global + * scheduler pool, maybe it'll be faster than discarding and + * allocating again. + */ + MEM_freeN(task); +#ifdef DEBUG_STATS + pool->mempool_stats[thread_id].num_discard++; +#endif + } +} + +/* Task Scheduler */ + +static void task_pool_num_decrease(TaskPool *pool, size_t done) +{ + BLI_mutex_lock(&pool->num_mutex); + + BLI_assert(pool->num >= done); + + pool->num -= done; + + if (pool->num == 0) { + BLI_condition_notify_all(&pool->num_cond); + } + + BLI_mutex_unlock(&pool->num_mutex); +} + +static void task_pool_num_increase(TaskPool *pool, size_t new_num) +{ + BLI_mutex_lock(&pool->num_mutex); + + pool->num += new_num; + BLI_condition_notify_all(&pool->num_cond); + + BLI_mutex_unlock(&pool->num_mutex); +} + +static bool task_scheduler_thread_wait_pop(TaskScheduler *scheduler, Task **task) +{ + bool found_task = false; + BLI_mutex_lock(&scheduler->queue_mutex); + + while (!scheduler->queue.first && !scheduler->do_exit) { + BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex); + } + + do { + Task *current_task; + + /* Assuming we can only have a void queue in 'exit' case here seems logical + * (we should only be here after our worker thread has been woken up from a + * condition_wait(), which only happens after a new task was added to the queue), + * but it is wrong. + * Waiting on condition may wake up the thread even if condition is not signaled + * (spurious wake-ups), and some race condition may also empty the queue **after** + * condition has been signaled, but **before** awoken thread reaches this point... + * See http://stackoverflow.com/questions/8594591 + * + * So we only abort here if do_exit is set. + */ + if (scheduler->do_exit) { + BLI_mutex_unlock(&scheduler->queue_mutex); + return false; + } + + for (current_task = (Task *)scheduler->queue.first; current_task != NULL; + current_task = current_task->next) { + TaskPool *pool = current_task->pool; + + if (scheduler->background_thread_only && !pool->run_in_background) { + continue; + } + + *task = current_task; + found_task = true; + BLI_remlink(&scheduler->queue, *task); + break; + } + if (!found_task) { + BLI_condition_wait(&scheduler->queue_cond, &scheduler->queue_mutex); + } + } while (!found_task); + + BLI_mutex_unlock(&scheduler->queue_mutex); + + return true; +} + +BLI_INLINE void handle_local_queue(TaskThreadLocalStorage *tls, const int thread_id) +{ + BLI_assert(!tls->do_delayed_push); + while (tls->num_local_queue > 0) { + /* We pop task from queue before handling it so handler of the task can + * push next job to the local queue. + */ + tls->num_local_queue--; + Task *local_task = tls->local_queue[tls->num_local_queue]; + /* TODO(sergey): Double-check work_and_wait() doesn't handle other's + * pool tasks. + */ + TaskPool *local_pool = local_task->pool; + local_task->run(local_pool, local_task->taskdata, thread_id); + task_free(local_pool, local_task, thread_id); + } + BLI_assert(!tls->do_delayed_push); +} + +static void *task_scheduler_thread_run(void *thread_p) +{ + TaskThread *thread = (TaskThread *)thread_p; + TaskThreadLocalStorage *tls = &thread->tls; + TaskScheduler *scheduler = thread->scheduler; + int thread_id = thread->id; + Task *task; + + pthread_setspecific(scheduler->tls_id_key, thread); + + /* signal the main thread when all threads have started */ + BLI_mutex_lock(&scheduler->startup_mutex); + scheduler->num_thread_started++; + if (scheduler->num_thread_started == scheduler->num_threads) { + BLI_condition_notify_one(&scheduler->startup_cond); + } + BLI_mutex_unlock(&scheduler->startup_mutex); + + /* keep popping off tasks */ + while (task_scheduler_thread_wait_pop(scheduler, &task)) { + TaskPool *pool = task->pool; + + /* run task */ + BLI_assert(!tls->do_delayed_push); + task->run(pool, task->taskdata, thread_id); + BLI_assert(!tls->do_delayed_push); + + /* delete task */ + task_free(pool, task, thread_id); + + /* Handle all tasks from local queue. */ + handle_local_queue(tls, thread_id); + + /* notify pool task was done */ + task_pool_num_decrease(pool, 1); + } + + return NULL; +} + +TaskScheduler *BLI_task_scheduler_create(int num_threads) +{ + TaskScheduler *scheduler = (TaskScheduler *)MEM_callocN(sizeof(TaskScheduler), "TaskScheduler"); + + /* multiple places can use this task scheduler, sharing the same + * threads, so we keep track of the number of users. */ + scheduler->do_exit = false; + + BLI_listbase_clear(&scheduler->queue); + BLI_mutex_init(&scheduler->queue_mutex); + BLI_condition_init(&scheduler->queue_cond); + + BLI_mutex_init(&scheduler->startup_mutex); + BLI_condition_init(&scheduler->startup_cond); + scheduler->num_thread_started = 0; + + if (num_threads == 0) { + /* automatic number of threads will be main thread + num cores */ + num_threads = BLI_system_thread_count(); + } + + /* main thread will also work, so we count it too */ + num_threads -= 1; + + /* Add background-only thread if needed. */ + if (num_threads == 0) { + scheduler->background_thread_only = true; + num_threads = 1; + } + + scheduler->task_threads = (TaskThread *)MEM_mallocN(sizeof(TaskThread) * (num_threads + 1), + "TaskScheduler task threads"); + + /* Initialize TLS for main thread. */ + initialize_task_tls(&scheduler->task_threads[0].tls); + + pthread_key_create(&scheduler->tls_id_key, NULL); + + /* launch threads that will be waiting for work */ + if (num_threads > 0) { + int i; + + scheduler->num_threads = num_threads; + scheduler->threads = (pthread_t *)MEM_callocN(sizeof(pthread_t) * num_threads, + "TaskScheduler threads"); + + for (i = 0; i < num_threads; i++) { + TaskThread *thread = &scheduler->task_threads[i + 1]; + thread->scheduler = scheduler; + thread->id = i + 1; + initialize_task_tls(&thread->tls); + + if (pthread_create(&scheduler->threads[i], NULL, task_scheduler_thread_run, thread) != 0) { + fprintf(stderr, "TaskScheduler failed to launch thread %d/%d\n", i, num_threads); + } + } + } + + /* Wait for all worker threads to start before returning to caller to prevent the case where + * threads are still starting and pthread_join is called, which causes a deadlock on pthreads4w. + */ + BLI_mutex_lock(&scheduler->startup_mutex); + /* NOTE: Use loop here to avoid false-positive everything-is-ready caused by spontaneous thread + * wake up. */ + while (scheduler->num_thread_started != num_threads) { + BLI_condition_wait(&scheduler->startup_cond, &scheduler->startup_mutex); + } + BLI_mutex_unlock(&scheduler->startup_mutex); + + return scheduler; +} + +void BLI_task_scheduler_free(TaskScheduler *scheduler) +{ + Task *task; + + /* stop all waiting threads */ + BLI_mutex_lock(&scheduler->queue_mutex); + scheduler->do_exit = true; + BLI_condition_notify_all(&scheduler->queue_cond); + BLI_mutex_unlock(&scheduler->queue_mutex); + + pthread_key_delete(scheduler->tls_id_key); + + /* delete threads */ + if (scheduler->threads) { + int i; + + for (i = 0; i < scheduler->num_threads; i++) { + if (pthread_join(scheduler->threads[i], NULL) != 0) { + fprintf(stderr, "TaskScheduler failed to join thread %d/%d\n", i, scheduler->num_threads); + } + } + + MEM_freeN(scheduler->threads); + } + + /* Delete task thread data */ + if (scheduler->task_threads) { + for (int i = 0; i < scheduler->num_threads + 1; i++) { + TaskThreadLocalStorage *tls = &scheduler->task_threads[i].tls; + free_task_tls(tls); + } + + MEM_freeN(scheduler->task_threads); + } + + /* delete leftover tasks */ + for (task = (Task *)scheduler->queue.first; task; task = task->next) { + task_data_free(task, 0); + } + BLI_freelistN(&scheduler->queue); + + /* delete mutex/condition */ + BLI_mutex_end(&scheduler->queue_mutex); + BLI_condition_end(&scheduler->queue_cond); + BLI_mutex_end(&scheduler->startup_mutex); + BLI_condition_end(&scheduler->startup_cond); + + MEM_freeN(scheduler); +} + +int BLI_task_scheduler_num_threads(TaskScheduler *scheduler) +{ + return scheduler->num_threads + 1; +} + +static void task_scheduler_push(TaskScheduler *scheduler, Task *task, TaskPriority priority) +{ + task_pool_num_increase(task->pool, 1); + + /* add task to queue */ + BLI_mutex_lock(&scheduler->queue_mutex); + + if (priority == TASK_PRIORITY_HIGH) { + BLI_addhead(&scheduler->queue, task); + } + else { + BLI_addtail(&scheduler->queue, task); + } + + BLI_condition_notify_one(&scheduler->queue_cond); + BLI_mutex_unlock(&scheduler->queue_mutex); +} + +static void task_scheduler_push_all(TaskScheduler *scheduler, + TaskPool *pool, + Task **tasks, + int num_tasks) +{ + if (num_tasks == 0) { + return; + } + + task_pool_num_increase(pool, num_tasks); + + BLI_mutex_lock(&scheduler->queue_mutex); + + for (int i = 0; i < num_tasks; i++) { + BLI_addhead(&scheduler->queue, tasks[i]); + } + + BLI_condition_notify_all(&scheduler->queue_cond); + BLI_mutex_unlock(&scheduler->queue_mutex); +} + +static void task_scheduler_clear(TaskScheduler *scheduler, TaskPool *pool) +{ + Task *task, *nexttask; + size_t done = 0; + + BLI_mutex_lock(&scheduler->queue_mutex); + + /* free all tasks from this pool from the queue */ + for (task = (Task *)scheduler->queue.first; task; task = nexttask) { + nexttask = task->next; + + if (task->pool == pool) { + task_data_free(task, pool->thread_id); + BLI_freelinkN(&scheduler->queue, task); + + done++; + } + } + + BLI_mutex_unlock(&scheduler->queue_mutex); + + /* notify done */ + task_pool_num_decrease(pool, done); +} + +/* Task Pool */ + +static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, + void *userdata, + const bool is_background, + const bool is_suspended, + TaskPriority priority) +{ + TaskPool *pool = (TaskPool *)MEM_mallocN(sizeof(TaskPool), "TaskPool"); + +#ifndef NDEBUG + /* Assert we do not try to create a background pool from some parent task - + * those only work OK from main thread. */ + if (is_background) { + const pthread_t thread_id = pthread_self(); + int i = scheduler->num_threads; + + while (i--) { + BLI_assert(!pthread_equal(scheduler->threads[i], thread_id)); + } + } +#endif + + pool->scheduler = scheduler; + pool->num = 0; + pool->do_cancel = false; + pool->do_work = false; + pool->is_suspended = is_suspended; + pool->start_suspended = is_suspended; + pool->num_suspended = 0; + pool->suspended_queue.first = pool->suspended_queue.last = NULL; + pool->priority = priority; + pool->run_in_background = is_background; + pool->use_local_tls = false; + + BLI_mutex_init(&pool->num_mutex); + BLI_condition_init(&pool->num_cond); + + pool->userdata = userdata; + BLI_mutex_init(&pool->user_mutex); + + if (BLI_thread_is_main()) { + pool->thread_id = 0; + } + else { + TaskThread *thread = (TaskThread *)pthread_getspecific(scheduler->tls_id_key); + if (thread == NULL) { + /* NOTE: Task pool is created from non-main thread which is not + * managed by the task scheduler. We identify ourselves as thread ID + * 0 but we do not use scheduler's TLS storage and use our own + * instead to avoid any possible threading conflicts. + */ + pool->thread_id = 0; + pool->use_local_tls = true; +#ifndef NDEBUG + pool->creator_thread_id = pthread_self(); +#endif + initialize_task_tls(&pool->local_tls); + } + else { + pool->thread_id = thread->id; + } + } + +#ifdef DEBUG_STATS + pool->mempool_stats = (TaskMemPoolStats *)MEM_callocN( + sizeof(*pool->mempool_stats) * (scheduler->num_threads + 1), "per-taskpool mempool stats"); +#endif + + /* Ensure malloc will go fine from threads, + * + * This is needed because we could be in main thread here + * and malloc could be non-thread safe at this point because + * no other jobs are running. + */ + BLI_threaded_malloc_begin(); + + return pool; +} + +/** + * Create a normal task pool. Tasks will be executed as soon as they are added. + */ +TaskPool *BLI_task_pool_create(TaskScheduler *scheduler, void *userdata, TaskPriority priority) +{ + return task_pool_create_ex(scheduler, userdata, false, false, priority); +} + +/** + * Create a background task pool. + * In multi-threaded context, there is no differences with #BLI_task_pool_create(), + * but in single-threaded case it is ensured to have at least one worker thread to run on + * (i.e. you don't have to call #BLI_task_pool_work_and_wait + * on it to be sure it will be processed). + * + * \note Background pools are non-recursive + * (that is, you should not create other background pools in tasks assigned to a background pool, + * they could end never being executed, since the 'fallback' background thread is already + * busy with parent task in single-threaded context). + */ +TaskPool *BLI_task_pool_create_background(TaskScheduler *scheduler, + void *userdata, + TaskPriority priority) +{ + return task_pool_create_ex(scheduler, userdata, true, false, priority); +} + +/** + * Similar to BLI_task_pool_create() but does not schedule any tasks for execution + * for until BLI_task_pool_work_and_wait() is called. This helps reducing threading + * overhead when pushing huge amount of small initial tasks from the main thread. + */ +TaskPool *BLI_task_pool_create_suspended(TaskScheduler *scheduler, + void *userdata, + TaskPriority priority) +{ + return task_pool_create_ex(scheduler, userdata, false, true, priority); +} + +void BLI_task_pool_free(TaskPool *pool) +{ + BLI_task_pool_cancel(pool); + + BLI_mutex_end(&pool->num_mutex); + BLI_condition_end(&pool->num_cond); + + BLI_mutex_end(&pool->user_mutex); + +#ifdef DEBUG_STATS + printf("Thread ID Allocated Reused Discarded\n"); + for (int i = 0; i < pool->scheduler->num_threads + 1; i++) { + printf("%02d %05d %05d %05d\n", + i, + pool->mempool_stats[i].num_alloc, + pool->mempool_stats[i].num_reuse, + pool->mempool_stats[i].num_discard); + } + MEM_freeN(pool->mempool_stats); +#endif + + if (pool->use_local_tls) { + free_task_tls(&pool->local_tls); + } + + MEM_freeN(pool); + + BLI_threaded_malloc_end(); +} + +BLI_INLINE bool task_can_use_local_queues(TaskPool *pool, int thread_id) +{ + return (thread_id != -1 && (thread_id != pool->thread_id || pool->do_work)); +} + +static void task_pool_push(TaskPool *pool, + TaskRunFunction run, + void *taskdata, + bool free_taskdata, + TaskFreeFunction freedata, + int thread_id) +{ + /* Allocate task and fill it's properties. */ + Task *task = task_alloc(pool, thread_id); + task->run = run; + task->taskdata = taskdata; + task->free_taskdata = free_taskdata; + task->freedata = freedata; + task->pool = pool; + /* For suspended pools we put everything yo a global queue first + * and exit as soon as possible. + * + * This tasks will be moved to actual execution when pool is + * activated by work_and_wait(). + */ + if (pool->is_suspended) { + BLI_addhead(&pool->suspended_queue, task); + atomic_fetch_and_add_z(&pool->num_suspended, 1); + return; + } + /* Populate to any local queue first, this is cheapest push ever. */ + if (task_can_use_local_queues(pool, thread_id)) { + ASSERT_THREAD_ID(pool->scheduler, thread_id); + TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); + /* Try to push to a local execution queue. + * These tasks will be picked up next. + */ + if (tls->num_local_queue < LOCAL_QUEUE_SIZE) { + tls->local_queue[tls->num_local_queue] = task; + tls->num_local_queue++; + return; + } + /* If we are in the delayed tasks push mode, we push tasks to a + * temporary local queue first without any locks, and then move them + * to global execution queue with a single lock. + */ + if (tls->do_delayed_push && tls->num_delayed_queue < DELAYED_QUEUE_SIZE) { + tls->delayed_queue[tls->num_delayed_queue] = task; + tls->num_delayed_queue++; + return; + } + } + /* Do push to a global execution pool, slowest possible method, + * causes quite reasonable amount of threading overhead. + */ + task_scheduler_push(pool->scheduler, task, pool->priority); +} + +void BLI_task_pool_push(TaskPool *pool, + TaskRunFunction run, + void *taskdata, + bool free_taskdata, + TaskFreeFunction freedata) +{ + task_pool_push(pool, run, taskdata, free_taskdata, freedata, -1); +} + +void BLI_task_pool_push_from_thread(TaskPool *pool, + TaskRunFunction run, + void *taskdata, + bool free_taskdata, + TaskFreeFunction freedata, + int thread_id) +{ + task_pool_push(pool, run, taskdata, free_taskdata, freedata, thread_id); +} + +void BLI_task_pool_work_and_wait(TaskPool *pool) +{ + TaskThreadLocalStorage *tls = get_task_tls(pool, pool->thread_id); + TaskScheduler *scheduler = pool->scheduler; + + if (atomic_fetch_and_and_uint8((uint8_t *)&pool->is_suspended, 0)) { + if (pool->num_suspended) { + task_pool_num_increase(pool, pool->num_suspended); + BLI_mutex_lock(&scheduler->queue_mutex); + + BLI_movelisttolist(&scheduler->queue, &pool->suspended_queue); + + BLI_condition_notify_all(&scheduler->queue_cond); + BLI_mutex_unlock(&scheduler->queue_mutex); + + pool->num_suspended = 0; + } + } + + pool->do_work = true; + + ASSERT_THREAD_ID(pool->scheduler, pool->thread_id); + + handle_local_queue(tls, pool->thread_id); + + BLI_mutex_lock(&pool->num_mutex); + + while (pool->num != 0) { + Task *task, *work_task = NULL; + bool found_task = false; + + BLI_mutex_unlock(&pool->num_mutex); + + BLI_mutex_lock(&scheduler->queue_mutex); + + /* find task from this pool. if we get a task from another pool, + * we can get into deadlock */ + + for (task = (Task *)scheduler->queue.first; task; task = task->next) { + if (task->pool == pool) { + work_task = task; + found_task = true; + BLI_remlink(&scheduler->queue, task); + break; + } + } + + BLI_mutex_unlock(&scheduler->queue_mutex); + + /* if found task, do it, otherwise wait until other tasks are done */ + if (found_task) { + /* run task */ + BLI_assert(!tls->do_delayed_push); + work_task->run(pool, work_task->taskdata, pool->thread_id); + BLI_assert(!tls->do_delayed_push); + + /* delete task */ + task_free(pool, task, pool->thread_id); + + /* Handle all tasks from local queue. */ + handle_local_queue(tls, pool->thread_id); + + /* notify pool task was done */ + task_pool_num_decrease(pool, 1); + } + + BLI_mutex_lock(&pool->num_mutex); + if (pool->num == 0) { + break; + } + + if (!found_task) { + BLI_condition_wait(&pool->num_cond, &pool->num_mutex); + } + } + + BLI_mutex_unlock(&pool->num_mutex); + + BLI_assert(tls->num_local_queue == 0); +} + +void BLI_task_pool_work_wait_and_reset(TaskPool *pool) +{ + BLI_task_pool_work_and_wait(pool); + + pool->do_work = false; + pool->is_suspended = pool->start_suspended; +} + +void BLI_task_pool_cancel(TaskPool *pool) +{ + pool->do_cancel = true; + + task_scheduler_clear(pool->scheduler, pool); + + /* wait until all entries are cleared */ + BLI_mutex_lock(&pool->num_mutex); + while (pool->num) { + BLI_condition_wait(&pool->num_cond, &pool->num_mutex); + } + BLI_mutex_unlock(&pool->num_mutex); + + pool->do_cancel = false; +} + +bool BLI_task_pool_canceled(TaskPool *pool) +{ + return pool->do_cancel; +} + +void *BLI_task_pool_userdata(TaskPool *pool) +{ + return pool->userdata; +} + +ThreadMutex *BLI_task_pool_user_mutex(TaskPool *pool) +{ + return &pool->user_mutex; +} + +int BLI_task_pool_creator_thread_id(TaskPool *pool) +{ + return pool->thread_id; +} + +void BLI_task_pool_delayed_push_begin(TaskPool *pool, int thread_id) +{ + if (task_can_use_local_queues(pool, thread_id)) { + ASSERT_THREAD_ID(pool->scheduler, thread_id); + TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); + tls->do_delayed_push = true; + } +} + +void BLI_task_pool_delayed_push_end(TaskPool *pool, int thread_id) +{ + if (task_can_use_local_queues(pool, thread_id)) { + ASSERT_THREAD_ID(pool->scheduler, thread_id); + TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id); + BLI_assert(tls->do_delayed_push); + task_scheduler_push_all(pool->scheduler, pool, tls->delayed_queue, tls->num_delayed_queue); + tls->do_delayed_push = false; + tls->num_delayed_queue = 0; + } +} diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 4540cda7e99..c7f3c5cb2b6 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -72,8 +72,7 @@ void schedule_children(DepsgraphEvalState *state, void schedule_node_to_pool(OperationNode *node, const int thread_id, TaskPool *pool) { - BLI_task_pool_push_from_thread( - pool, deg_task_run_func, node, false, TASK_PRIORITY_HIGH, thread_id); + BLI_task_pool_push_from_thread(pool, deg_task_run_func, node, false, NULL, thread_id); } /* Denotes which part of dependency graph is being evaluated. */ @@ -389,7 +388,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph) task_scheduler = BLI_task_scheduler_get(); need_free_scheduler = false; } - TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state); + TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state, TASK_PRIORITY_HIGH); /* Prepare all nodes for evaluation. */ initialize_execution(&state, graph); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 0449601adf7..fe63dec1294 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -4524,7 +4524,7 @@ static void extract_range_task_create( taskdata->iter_type = type; taskdata->start = start; taskdata->end = start + length; - BLI_task_pool_push(task_pool, extract_run, taskdata, true, TASK_PRIORITY_HIGH); + BLI_task_pool_push(task_pool, extract_run, taskdata, true, NULL); } static void extract_task_create(TaskPool *task_pool, @@ -4583,7 +4583,7 @@ static void extract_task_create(TaskPool *task_pool, else if (use_thread) { /* One task for the whole VBO. */ (*task_counter)++; - BLI_task_pool_push(task_pool, extract_run, taskdata, true, TASK_PRIORITY_HIGH); + BLI_task_pool_push(task_pool, extract_run, taskdata, true, NULL); } else { /* Single threaded extraction. */ @@ -4682,7 +4682,7 @@ void mesh_buffer_cache_create_requested(MeshBatchCache *cache, TaskPool *task_pool; task_scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create_suspended(task_scheduler, NULL); + task_pool = BLI_task_pool_create_suspended(task_scheduler, NULL, TASK_PRIORITY_HIGH); size_t counters_size = (sizeof(mbc) / sizeof(void *)) * sizeof(int32_t); int32_t *task_counters = MEM_callocN(counters_size, __func__); diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 1f857ad4710..48ec41027ff 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -542,15 +542,15 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) # ifdef USE_ARRAY_STORE_THREAD if (um_arraystore.task_pool == NULL) { TaskScheduler *scheduler = BLI_task_scheduler_get(); - um_arraystore.task_pool = BLI_task_pool_create_background(scheduler, NULL); + um_arraystore.task_pool = BLI_task_pool_create_background( + scheduler, NULL, TASK_PRIORITY_LOW); } struct UMArrayData *um_data = MEM_mallocN(sizeof(*um_data), __func__); um_data->um = um; um_data->um_ref = um_ref; - BLI_task_pool_push( - um_arraystore.task_pool, um_arraystore_compact_cb, um_data, true, TASK_PRIORITY_LOW); + BLI_task_pool_push(um_arraystore.task_pool, um_arraystore_compact_cb, um_data, true, NULL); # else um_arraystore_compact_with_info(um, um_ref); # endif diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index c23ce8a8a9e..f35f92feaec 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -859,11 +859,12 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { task_scheduler = BLI_task_scheduler_create(1); oglrender->task_scheduler = task_scheduler; - oglrender->task_pool = BLI_task_pool_create_background(task_scheduler, oglrender); + oglrender->task_pool = BLI_task_pool_create_background( + task_scheduler, oglrender, TASK_PRIORITY_LOW); } else { oglrender->task_scheduler = NULL; - oglrender->task_pool = BLI_task_pool_create(task_scheduler, oglrender); + oglrender->task_pool = BLI_task_pool_create(task_scheduler, oglrender, TASK_PRIORITY_LOW); } oglrender->pool_ok = true; BLI_spin_init(&oglrender->reports_lock); @@ -1123,7 +1124,7 @@ static bool schedule_write_result(OGLRender *oglrender, RenderResult *rr) BLI_condition_wait(&oglrender->task_condition, &oglrender->task_mutex); } BLI_mutex_unlock(&oglrender->task_mutex); - BLI_task_pool_push(oglrender->task_pool, write_result_func, task_data, true, TASK_PRIORITY_LOW); + BLI_task_pool_push(oglrender->task_pool, write_result_func, task_data, true, NULL); return true; } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 0b99806109a..6af9ec01fc3 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -5617,7 +5617,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po if (ps->thread_tot > 1) { scheduler = BLI_task_scheduler_get(); - task_pool = BLI_task_pool_create_suspended(scheduler, NULL); + task_pool = BLI_task_pool_create_suspended(scheduler, NULL, TASK_PRIORITY_HIGH); } image_pool = BKE_image_pool_new(); @@ -5652,8 +5652,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po handles[a].pool = image_pool; if (task_pool != NULL) { - BLI_task_pool_push( - task_pool, do_projectpaint_thread, &handles[a], false, TASK_PRIORITY_HIGH); + BLI_task_pool_push(task_pool, do_projectpaint_thread, &handles[a], false, NULL); } } diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index ffc50407917..cce01947ab7 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -961,9 +961,9 @@ static void start_prefetch_threads(MovieClip *clip, queue.do_update = do_update; queue.progress = progress; - task_pool = BLI_task_pool_create(task_scheduler, &queue); + task_pool = BLI_task_pool_create(task_scheduler, &queue, TASK_PRIORITY_LOW); for (i = 0; i < tot_thread; i++) { - BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, NULL); } BLI_task_pool_work_and_wait(task_pool); BLI_task_pool_free(task_pool); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 266212fdfd7..3204374b747 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -1434,7 +1434,7 @@ static void do_sequence_proxy(void *pjv, queue.do_update = do_update; queue.progress = progress; - task_pool = BLI_task_pool_create(task_scheduler, &queue); + task_pool = BLI_task_pool_create(task_scheduler, &queue, TASK_PRIORITY_LOW); handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles"); for (i = 0; i < tot_thread; i++) { ProxyThread *handle = &handles[i]; @@ -1451,7 +1451,7 @@ static void do_sequence_proxy(void *pjv, handle->distortion = BKE_tracking_distortion_new(&clip->tracking, width, height); } - BLI_task_pool_push(task_pool, proxy_task_func, handle, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, proxy_task_func, handle, false, NULL); } BLI_task_pool_work_and_wait(task_pool); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 778c27e1a17..ac8fa413f07 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1329,7 +1329,7 @@ static void filelist_cache_preview_ensure_running(FileListEntryCache *cache) if (!cache->previews_pool) { TaskScheduler *scheduler = BLI_task_scheduler_get(); - cache->previews_pool = BLI_task_pool_create_background(scheduler, cache); + cache->previews_pool = BLI_task_pool_create_background(scheduler, cache, TASK_PRIORITY_LOW); cache->previews_done = BLI_thread_queue_init(); IMB_thumb_locks_acquire(); @@ -1393,12 +1393,11 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), __func__); preview_taskdata->preview = preview; - BLI_task_pool_push_ex(cache->previews_pool, - filelist_cache_preview_runf, - preview_taskdata, - true, - filelist_cache_preview_freef, - TASK_PRIORITY_LOW); + BLI_task_pool_push(cache->previews_pool, + filelist_cache_preview_runf, + preview_taskdata, + true, + filelist_cache_preview_freef); } } diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 523b57cc162..aa82487d69f 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -360,7 +360,7 @@ void IMB_processor_apply_threaded( int total_tasks = (buffer_lines + lines_per_task - 1) / lines_per_task; int i, start_line; - task_pool = BLI_task_pool_create(task_scheduler, do_thread); + task_pool = BLI_task_pool_create(task_scheduler, do_thread, TASK_PRIORITY_LOW); handles = MEM_callocN(handle_size * total_tasks, "processor apply threaded handles"); @@ -379,7 +379,7 @@ void IMB_processor_apply_threaded( init_handle(handle, start_line, lines_per_current_task, init_customdata); - BLI_task_pool_push(task_pool, processor_apply_func, handle, false, TASK_PRIORITY_LOW); + BLI_task_pool_push(task_pool, processor_apply_func, handle, false, NULL); start_line += lines_per_task; } @@ -421,13 +421,10 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines, data.total_scanlines = total_scanlines; const int total_tasks = (total_scanlines + scanlines_per_task - 1) / scanlines_per_task; TaskScheduler *task_scheduler = BLI_task_scheduler_get(); - TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &data); + TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &data, TASK_PRIORITY_LOW); for (int i = 0, start_line = 0; i < total_tasks; i++) { - BLI_task_pool_push(task_pool, - processor_apply_scanline_func, - POINTER_FROM_INT(start_line), - false, - TASK_PRIORITY_LOW); + BLI_task_pool_push( + task_pool, processor_apply_scanline_func, POINTER_FROM_INT(start_line), false, NULL); start_line += scanlines_per_task; } diff --git a/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc b/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc index f1bd02e0d9e..9934d1a3337 100644 --- a/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc +++ b/tests/gtests/blenlib/BLI_linklist_lockfree_test.cc @@ -83,10 +83,10 @@ TEST(LockfreeLinkList, InsertMultipleConcurrent) BLI_linklist_lockfree_init(&list); /* Initialize task scheduler and pool. */ TaskScheduler *scheduler = BLI_task_scheduler_create(num_threads); - TaskPool *pool = BLI_task_pool_create_suspended(scheduler, &list); + TaskPool *pool = BLI_task_pool_create_suspended(scheduler, &list, TASK_PRIORITY_HIGH); /* Push tasks to the pool. */ for (int i = 0; i < num_nodes; ++i) { - BLI_task_pool_push(pool, concurrent_insert, POINTER_FROM_INT(i), false, TASK_PRIORITY_HIGH); + BLI_task_pool_push(pool, concurrent_insert, POINTER_FROM_INT(i), false, NULL); } /* Run all the tasks. */ BLI_threaded_malloc_begin(); -- cgit v1.2.3 From 07bb7206c202fdea3645b01887e9eeba3d2e6ae3 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 9 Apr 2020 19:16:36 +0200 Subject: Cleanup: VSE rename i to view_id for stereo 3d code --- source/blender/blenkernel/intern/sequencer.c | 126 ++++++++++++++------------- 1 file changed, 66 insertions(+), 60 deletions(-) diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index bb1d8cb2e9b..53d592326dd 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -3026,7 +3026,6 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, struct ImBuf **ibufs_arr; char prefix[FILE_MAX]; const char *ext = NULL; - int i; if (totfiles > 1) { BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext); @@ -3041,21 +3040,21 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, totviews = BKE_scene_multiview_num_views_get(&context->scene->r); ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); - for (i = 0; i < totfiles; i++) { + for (int view_id = 0; view_id < totfiles; view_id++) { if (prefix[0] == '\0') { - ibufs_arr[i] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name); + ibufs_arr[view_id] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name); } else { char str[FILE_MAX]; - seq_multiview_name(context->scene, i, prefix, ext, str, FILE_MAX); - ibufs_arr[i] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name); + seq_multiview_name(context->scene, view_id, prefix, ext, str, FILE_MAX); + ibufs_arr[view_id] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name); } - if (ibufs_arr[i]) { + if (ibufs_arr[view_id]) { /* we don't need both (speed reasons)! */ - if (ibufs_arr[i]->rect_float && ibufs_arr[i]->rect) { - imb_freerectImBuf(ibufs_arr[i]); + if (ibufs_arr[view_id]->rect_float && ibufs_arr[view_id]->rect) { + imb_freerectImBuf(ibufs_arr[view_id]); } } } @@ -3064,17 +3063,22 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, IMB_ImBufFromStereo3d(seq->stereo3d_format, ibufs_arr[0], &ibufs_arr[0], &ibufs_arr[1]); } - for (i = 0; i < totviews; i++) { - if (ibufs_arr[i]) { + for (int view_id = 0; view_id < totviews; view_id++) { + if (ibufs_arr[view_id]) { SeqRenderData localcontext = *context; - localcontext.view_id = i; + localcontext.view_id = view_id; /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); - - if (i != context->view_id) { - BKE_sequencer_cache_put( - &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibufs_arr[i], 0, false); + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[view_id], false); + + if (view_id != context->view_id) { + BKE_sequencer_cache_put(&localcontext, + seq, + cfra, + SEQ_CACHE_STORE_PREPROCESSED, + ibufs_arr[view_id], + 0, + false); } } } @@ -3087,9 +3091,9 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, } /* "remove" the others (decrease their refcount) */ - for (i = 0; i < totviews; i++) { - if (ibufs_arr[i] != ibuf) { - IMB_freeImBuf(ibufs_arr[i]); + for (int view_id = 0; view_id < totviews; view_id++) { + if (ibufs_arr[view_id] != ibuf) { + IMB_freeImBuf(ibufs_arr[view_id]); } } @@ -3137,7 +3141,7 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, ImBuf **ibuf_arr; const int totfiles = seq_num_files(context->scene, seq->views_format, true); int totviews; - int i; + int ibuf_view_id; if (totfiles != BLI_listbase_count_at_most(&seq->anims, totfiles + 1)) { goto monoview_movie; @@ -3146,28 +3150,28 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, totviews = BKE_scene_multiview_num_views_get(&context->scene->r); ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); - for (i = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, i++) { + for (ibuf_view_id = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, ibuf_view_id++) { if (sanim->anim) { IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); - ibuf_arr[i] = IMB_anim_absolute(sanim->anim, - nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : - IMB_TC_RECORD_RUN, - psize); + ibuf_arr[ibuf_view_id] = IMB_anim_absolute(sanim->anim, + nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : + IMB_TC_RECORD_RUN, + psize); /* fetching for requested proxy size failed, try fetching the original instead */ - if (!ibuf_arr[i] && psize != IMB_PROXY_NONE) { - ibuf_arr[i] = IMB_anim_absolute(sanim->anim, - nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : - IMB_TC_RECORD_RUN, - IMB_PROXY_NONE); + if (!ibuf_arr[ibuf_view_id] && psize != IMB_PROXY_NONE) { + ibuf_arr[ibuf_view_id] = IMB_anim_absolute(sanim->anim, + nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : + IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); } - if (ibuf_arr[i]) { + if (ibuf_arr[ibuf_view_id]) { /* we don't need both (speed reasons)! */ - if (ibuf_arr[i]->rect_float && ibuf_arr[i]->rect) { - imb_freerectImBuf(ibuf_arr[i]); + if (ibuf_arr[ibuf_view_id]->rect_float && ibuf_arr[ibuf_view_id]->rect) { + imb_freerectImBuf(ibuf_arr[ibuf_view_id]); } } } @@ -3184,17 +3188,17 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, } } - for (i = 0; i < totviews; i++) { + for (int view_id = 0; view_id < totviews; view_id++) { SeqRenderData localcontext = *context; - localcontext.view_id = i; + localcontext.view_id = view_id; - if (ibuf_arr[i]) { + if (ibuf_arr[view_id]) { /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[i], false); + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[view_id], false); } - if (i != context->view_id) { + if (view_id != context->view_id) { BKE_sequencer_cache_put( - &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf_arr[i], 0, false); + &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf_arr[view_id], 0, false); } } @@ -3206,9 +3210,9 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, } /* "remove" the others (decrease their refcount) */ - for (i = 0; i < totviews; i++) { - if (ibuf_arr[i] != ibuf) { - IMB_freeImBuf(ibuf_arr[i]); + for (int view_id = 0; view_id < totviews; view_id++) { + if (ibuf_arr[view_id] != ibuf) { + IMB_freeImBuf(ibuf_arr[view_id]); } } @@ -3542,7 +3546,6 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, else { Render *re = RE_GetSceneRender(scene); const int totviews = BKE_scene_multiview_num_views_get(&scene->r); - int i; ImBuf **ibufs_arr; ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); @@ -3567,34 +3570,37 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, G.is_rendering = is_rendering; } - for (i = 0; i < totviews; i++) { + for (int view_id = 0; view_id < totviews; view_id++) { SeqRenderData localcontext = *context; RenderResult rres; - localcontext.view_id = i; + localcontext.view_id = view_id; - RE_AcquireResultImage(re, &rres, i); + RE_AcquireResultImage(re, &rres, view_id); if (rres.rectf) { - ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat); - memcpy(ibufs_arr[i]->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty); + ibufs_arr[view_id] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat); + memcpy(ibufs_arr[view_id]->rect_float, + rres.rectf, + 4 * sizeof(float) * rres.rectx * rres.recty); if (rres.rectz) { - addzbuffloatImBuf(ibufs_arr[i]); - memcpy(ibufs_arr[i]->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty); + addzbuffloatImBuf(ibufs_arr[view_id]); + memcpy( + ibufs_arr[view_id]->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty); } /* float buffers in the sequencer are not linear */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[view_id], false); } else if (rres.rect32) { - ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); - memcpy(ibufs_arr[i]->rect, rres.rect32, 4 * rres.rectx * rres.recty); + ibufs_arr[view_id] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); + memcpy(ibufs_arr[view_id]->rect, rres.rect32, 4 * rres.rectx * rres.recty); } - if (i != context->view_id) { + if (view_id != context->view_id) { BKE_sequencer_cache_put( - &localcontext, seq, cfra, SEQ_CACHE_STORE_RAW, ibufs_arr[i], 0, false); + &localcontext, seq, cfra, SEQ_CACHE_STORE_RAW, ibufs_arr[view_id], 0, false); } RE_ReleaseResultImage(re); @@ -3604,9 +3610,9 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, ibuf = ibufs_arr[context->view_id]; /* "remove" the others (decrease their refcount) */ - for (i = 0; i < totviews; i++) { - if (ibufs_arr[i] != ibuf) { - IMB_freeImBuf(ibufs_arr[i]); + for (int view_id = 0; view_id < totviews; view_id++) { + if (ibufs_arr[view_id] != ibuf) { + IMB_freeImBuf(ibufs_arr[view_id]); } } MEM_freeN(ibufs_arr); -- cgit v1.2.3 From f3433fcd3bf87c9405fb05c96fde036eb658e8aa Mon Sep 17 00:00:00 2001 From: Szymon Ulatowski Date: Thu, 9 Apr 2020 19:33:57 +0200 Subject: Collections: preserve exclude flag of child collections when unexcluding Excluding a collection also changes the exclude setting on all child collections so that it is possible to selectively enable some children without the parent being enabled. This change makes it so that if you unexclude the parent, the exclude setting of children are restored again instead of being permanently lost. Original patch by Szymon with modifications by Brecht. Differential Revision: https://developer.blender.org/D7016 --- source/blender/blenkernel/BKE_layer.h | 1 + source/blender/blenkernel/intern/layer.c | 43 ++++++++++++++++++++++ .../editors/space_outliner/outliner_collections.c | 27 +------------- source/blender/makesdna/DNA_layer_types.h | 1 + source/blender/makesrna/intern/rna_layer.c | 15 +------- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index 7059675ec7d..7a0252e6813 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -137,6 +137,7 @@ void BKE_layer_collection_set_visible(struct ViewLayer *view_layer, struct LayerCollection *lc, const bool visible, const bool hierarchy); +void BKE_layer_collection_set_flag(struct LayerCollection *lc, const int flag, const bool value); /* evaluation */ diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index d8f0bda8c22..f6e80d66ad1 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -1359,6 +1359,49 @@ void BKE_layer_collection_set_visible(ViewLayer *view_layer, } } +/** + * Set layer collection hide/exclude/indirect flag on a layer collection. + * recursively. + */ +static void layer_collection_flag_recursive_set(LayerCollection *lc, + const int flag, + const bool value, + const bool restore_flag) +{ + if (flag == LAYER_COLLECTION_EXCLUDE) { + /* For exclude flag, we remember the state the children had before + * excluding and restoring it when enabling the parent collection again. */ + if (value) { + if (restore_flag) { + SET_FLAG_FROM_TEST( + lc->flag, (lc->flag & LAYER_COLLECTION_EXCLUDE), LAYER_COLLECTION_PREVIOUSLY_EXCLUDED); + } + else { + lc->flag &= ~LAYER_COLLECTION_PREVIOUSLY_EXCLUDED; + } + + lc->flag |= flag; + } + else { + if (!(lc->flag & LAYER_COLLECTION_PREVIOUSLY_EXCLUDED)) { + lc->flag &= ~flag; + } + } + } + else { + SET_FLAG_FROM_TEST(lc->flag, value, flag); + } + + LISTBASE_FOREACH (LayerCollection *, nlc, &lc->layer_collections) { + layer_collection_flag_recursive_set(nlc, flag, value, true); + } +} + +void BKE_layer_collection_set_flag(LayerCollection *lc, const int flag, const bool value) +{ + layer_collection_flag_recursive_set(lc, flag, value, false); +} + /* ---------------------------------------------------------------------- */ static LayerCollection *find_layer_collection_by_scene_collection(LayerCollection *lc, diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 5a0ed954909..4b6241d45ee 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -845,20 +845,6 @@ static bool collections_indirect_only_clear_poll(bContext *C) return collections_view_layer_poll(C, true, LAYER_COLLECTION_INDIRECT_ONLY); } -static void layer_collection_flag_recursive_set(LayerCollection *lc, int flag) -{ - LISTBASE_FOREACH (LayerCollection *, nlc, &lc->layer_collections) { - if (lc->flag & flag) { - nlc->flag |= flag; - } - else { - nlc->flag &= ~flag; - } - - layer_collection_flag_recursive_set(nlc, flag); - } -} - static int collection_view_layer_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -883,15 +869,7 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op) GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter); - - if (clear) { - lc->flag &= ~flag; - } - else { - lc->flag |= flag; - } - - layer_collection_flag_recursive_set(lc, flag); + BKE_layer_collection_set_flag(lc, flag, !clear); } BLI_gset_free(data.collections_to_edit, NULL); @@ -1468,8 +1446,7 @@ static int outliner_unhide_all_exec(bContext *C, wmOperator *UNUSED(op)) /* Unhide all the collections. */ LayerCollection *lc_master = view_layer->layer_collections.first; LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) { - lc_iter->flag &= ~LAYER_COLLECTION_HIDE; - layer_collection_flag_recursive_set(lc_iter, LAYER_COLLECTION_HIDE); + BKE_layer_collection_set_flag(lc_iter, LAYER_COLLECTION_HIDE, false); } /* Unhide all objects. */ diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index a9293d18d41..4676e7fa313 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -173,6 +173,7 @@ enum { LAYER_COLLECTION_HOLDOUT = (1 << 5), LAYER_COLLECTION_INDIRECT_ONLY = (1 << 6), LAYER_COLLECTION_HIDE = (1 << 7), + LAYER_COLLECTION_PREVIOUSLY_EXCLUDED = (1 << 8), }; /* Layer Collection->runtime_flag diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index 46a32256114..b99457056fe 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -299,19 +299,6 @@ static void rna_LayerCollection_hide_viewport_set(PointerRNA *ptr, bool value) rna_LayerCollection_flag_set(ptr, value, LAYER_COLLECTION_HIDE); } -static void rna_LayerCollection_exclude_update_recursive(ListBase *lb, const bool exclude) -{ - LISTBASE_FOREACH (LayerCollection *, lc, lb) { - if (exclude) { - lc->flag |= LAYER_COLLECTION_EXCLUDE; - } - else { - lc->flag &= ~LAYER_COLLECTION_EXCLUDE; - } - rna_LayerCollection_exclude_update_recursive(&lc->layer_collections, exclude); - } -} - static void rna_LayerCollection_exclude_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; @@ -320,7 +307,7 @@ static void rna_LayerCollection_exclude_update(Main *bmain, Scene *UNUSED(scene) /* Set/Unset it recursively to match the behavior of excluding via the menu or shortcuts. */ const bool exclude = (lc->flag & LAYER_COLLECTION_EXCLUDE) != 0; - rna_LayerCollection_exclude_update_recursive(&lc->layer_collections, exclude); + BKE_layer_collection_set_flag(lc, LAYER_COLLECTION_EXCLUDE, exclude); BKE_layer_collection_sync(scene, view_layer); -- cgit v1.2.3 From 054950def946ed7638c2d9c18ef850cbba94d9d7 Mon Sep 17 00:00:00 2001 From: Bartosz Moniewski Date: Thu, 26 Mar 2020 14:43:53 +0100 Subject: Shading: add Roughness input to Noise and Wave texture nodes Currently in fractal_noise functions, each subsequent octave doubles the frequency and reduces the amplitude by half. This patch introduces Roughness input to Noise and Wave nodes. This multiplier determines how quickly the amplitudes of the subsequent octaves decrease. Value of 0.5 will be the default, generating identical noise we had before. Values above 0.5 will increase influence of each octave resulting in more "rough" noise, most interesting pattern changes happen there. Values below 0.5 will result in more "smooth" noise. Differential Revision: https://developer.blender.org/D7065 --- intern/cycles/kernel/shaders/node_noise.h | 52 +++++++------ .../cycles/kernel/shaders/node_noise_texture.osl | 44 ++++++----- intern/cycles/kernel/shaders/node_wave_texture.osl | 9 ++- intern/cycles/kernel/svm/svm_fractal_noise.h | 52 +++++++------ intern/cycles/kernel/svm/svm_noisetex.h | 91 ++++++++++++++-------- intern/cycles/kernel/svm/svm_wave.h | 32 ++++---- intern/cycles/render/nodes.cpp | 46 ++++++----- intern/cycles/render/nodes.h | 4 +- .../gpu_shader_material_fractal_noise.glsl | 52 +++++++------ .../material/gpu_shader_material_tex_noise.glsl | 64 ++++++++++----- .../material/gpu_shader_material_tex_wave.glsl | 5 +- .../nodes/shader/nodes/node_shader_tex_noise.c | 1 + .../nodes/shader/nodes/node_shader_tex_wave.c | 1 + 13 files changed, 272 insertions(+), 181 deletions(-) diff --git a/intern/cycles/kernel/shaders/node_noise.h b/intern/cycles/kernel/shaders/node_noise.h index 23d1987a00e..ab4cd7792cc 100644 --- a/intern/cycles/kernel/shaders/node_noise.h +++ b/intern/cycles/kernel/shaders/node_noise.h @@ -84,114 +84,118 @@ float safe_snoise(vector4 p) } /* The fractal_noise functions are all exactly the same except for the input type. */ -float fractal_noise(float p, float details) +float fractal_noise(float p, float details, float roughness) { float fscale = 1.0; float amp = 1.0; + float maxamp = 0.0; float sum = 0.0; float octaves = clamp(details, 0.0, 16.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); sum += t * amp; - amp *= 0.5; + maxamp += amp; + amp *= clamp(roughness, 0.0, 1.0); fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { float t = safe_noise(fscale * p); float sum2 = sum + t * amp; - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise functions are all exactly the same except for the input type. */ -float fractal_noise(vector2 p, float details) +float fractal_noise(vector2 p, float details, float roughness) { float fscale = 1.0; float amp = 1.0; + float maxamp = 0.0; float sum = 0.0; float octaves = clamp(details, 0.0, 16.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); sum += t * amp; - amp *= 0.5; + maxamp += amp; + amp *= clamp(roughness, 0.0, 1.0); fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { float t = safe_noise(fscale * p); float sum2 = sum + t * amp; - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise functions are all exactly the same except for the input type. */ -float fractal_noise(vector3 p, float details) +float fractal_noise(vector3 p, float details, float roughness) { float fscale = 1.0; float amp = 1.0; + float maxamp = 0.0; float sum = 0.0; float octaves = clamp(details, 0.0, 16.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); sum += t * amp; - amp *= 0.5; + maxamp += amp; + amp *= clamp(roughness, 0.0, 1.0); fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { float t = safe_noise(fscale * p); float sum2 = sum + t * amp; - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise functions are all exactly the same except for the input type. */ -float fractal_noise(vector4 p, float details) +float fractal_noise(vector4 p, float details, float roughness) { float fscale = 1.0; float amp = 1.0; + float maxamp = 0.0; float sum = 0.0; float octaves = clamp(details, 0.0, 16.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); sum += t * amp; - amp *= 0.5; + maxamp += amp; + amp *= clamp(roughness, 0.0, 1.0); fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { float t = safe_noise(fscale * p); float sum2 = sum + t * amp; - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } diff --git a/intern/cycles/kernel/shaders/node_noise_texture.osl b/intern/cycles/kernel/shaders/node_noise_texture.osl index 4121b415673..61c0216910b 100644 --- a/intern/cycles/kernel/shaders/node_noise_texture.osl +++ b/intern/cycles/kernel/shaders/node_noise_texture.osl @@ -55,21 +55,22 @@ vector4 random_vector4_offset(float seed) 100.0 + noise("hash", seed, 3.0) * 100.0); } -float noise_texture(float co, float detail, float distortion, output color Color) +float noise_texture(float co, float detail, float roughness, float distortion, output color Color) { float p = co; if (distortion != 0.0) { p += safe_snoise(p + random_float_offset(0.0)) * distortion; } - float value = fractal_noise(p, detail); + float value = fractal_noise(p, detail, roughness); Color = color(value, - fractal_noise(p + random_float_offset(1.0), detail), - fractal_noise(p + random_float_offset(2.0), detail)); + fractal_noise(p + random_float_offset(1.0), detail, roughness), + fractal_noise(p + random_float_offset(2.0), detail, roughness)); return value; } -float noise_texture(vector2 co, float detail, float distortion, output color Color) +float noise_texture( + vector2 co, float detail, float roughness, float distortion, output color Color) { vector2 p = co; if (distortion != 0.0) { @@ -77,14 +78,15 @@ float noise_texture(vector2 co, float detail, float distortion, output color Col safe_snoise(p + random_vector2_offset(1.0)) * distortion); } - float value = fractal_noise(p, detail); + float value = fractal_noise(p, detail, roughness); Color = color(value, - fractal_noise(p + random_vector2_offset(2.0), detail), - fractal_noise(p + random_vector2_offset(3.0), detail)); + fractal_noise(p + random_vector2_offset(2.0), detail, roughness), + fractal_noise(p + random_vector2_offset(3.0), detail, roughness)); return value; } -float noise_texture(vector3 co, float detail, float distortion, output color Color) +float noise_texture( + vector3 co, float detail, float roughness, float distortion, output color Color) { vector3 p = co; if (distortion != 0.0) { @@ -93,14 +95,15 @@ float noise_texture(vector3 co, float detail, float distortion, output color Col safe_snoise(p + random_vector3_offset(2.0)) * distortion); } - float value = fractal_noise(p, detail); + float value = fractal_noise(p, detail, roughness); Color = color(value, - fractal_noise(p + random_vector3_offset(3.0), detail), - fractal_noise(p + random_vector3_offset(4.0), detail)); + fractal_noise(p + random_vector3_offset(3.0), detail, roughness), + fractal_noise(p + random_vector3_offset(4.0), detail, roughness)); return value; } -float noise_texture(vector4 co, float detail, float distortion, output color Color) +float noise_texture( + vector4 co, float detail, float roughness, float distortion, output color Color) { vector4 p = co; if (distortion != 0.0) { @@ -110,10 +113,10 @@ float noise_texture(vector4 co, float detail, float distortion, output color Col safe_snoise(p + random_vector4_offset(3.0)) * distortion); } - float value = fractal_noise(p, detail); + float value = fractal_noise(p, detail, roughness); Color = color(value, - fractal_noise(p + random_vector4_offset(4.0), detail), - fractal_noise(p + random_vector4_offset(5.0), detail)); + fractal_noise(p + random_vector4_offset(4.0), detail, roughness), + fractal_noise(p + random_vector4_offset(5.0), detail, roughness)); return value; } @@ -124,6 +127,7 @@ shader node_noise_texture(int use_mapping = 0, float W = 0.0, float Scale = 5.0, float Detail = 2.0, + float Roughness = 0.5, float Distortion = 0.0, output float Fac = 0.0, output color Color = 0.0) @@ -136,13 +140,13 @@ shader node_noise_texture(int use_mapping = 0, float w = W * Scale; if (dimensions == "1D") - Fac = noise_texture(w, Detail, Distortion, Color); + Fac = noise_texture(w, Detail, Roughness, Distortion, Color); else if (dimensions == "2D") - Fac = noise_texture(vector2(p[0], p[1]), Detail, Distortion, Color); + Fac = noise_texture(vector2(p[0], p[1]), Detail, Roughness, Distortion, Color); else if (dimensions == "3D") - Fac = noise_texture(p, Detail, Distortion, Color); + Fac = noise_texture(p, Detail, Roughness, Distortion, Color); else if (dimensions == "4D") - Fac = noise_texture(vector4(p[0], p[1], p[2], w), Detail, Distortion, Color); + Fac = noise_texture(vector4(p[0], p[1], p[2], w), Detail, Roughness, Distortion, Color); else error("Unknown dimension!"); } diff --git a/intern/cycles/kernel/shaders/node_wave_texture.osl b/intern/cycles/kernel/shaders/node_wave_texture.osl index f17397be243..874bfb8d3af 100644 --- a/intern/cycles/kernel/shaders/node_wave_texture.osl +++ b/intern/cycles/kernel/shaders/node_wave_texture.osl @@ -24,9 +24,10 @@ float wave(point p_input, string bands_direction, string rings_direction, string profile, - float detail, float distortion, + float detail, float dscale, + float droughness, float phase) { /* Prevent precision issues on unit coordinates. */ @@ -67,7 +68,7 @@ float wave(point p_input, n += phase; if (distortion != 0.0) { - n = n + (distortion * (fractal_noise(p * dscale, detail) * 2.0 - 1.0)); + n = n + (distortion * (fractal_noise(p * dscale, detail, droughness) * 2.0 - 1.0)); } if (profile == "sine") { @@ -93,6 +94,7 @@ shader node_wave_texture(int use_mapping = 0, float Distortion = 0.0, float Detail = 2.0, float DetailScale = 1.0, + float DetailRoughness = 0.5, float PhaseOffset = 0.0, point Vector = P, output float Fac = 0.0, @@ -108,9 +110,10 @@ shader node_wave_texture(int use_mapping = 0, bands_direction, rings_direction, profile, - Detail, Distortion, + Detail, DetailScale, + DetailRoughness, PhaseOffset); Color = Fac; } diff --git a/intern/cycles/kernel/svm/svm_fractal_noise.h b/intern/cycles/kernel/svm/svm_fractal_noise.h index 5b2e4a28fce..57fa8c690ac 100644 --- a/intern/cycles/kernel/svm/svm_fractal_noise.h +++ b/intern/cycles/kernel/svm/svm_fractal_noise.h @@ -17,114 +17,118 @@ CCL_NAMESPACE_BEGIN /* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */ -ccl_device_noinline float fractal_noise_1d(float p, float octaves) +ccl_device_noinline float fractal_noise_1d(float p, float octaves, float roughness) { float fscale = 1.0f; float amp = 1.0f; + float maxamp = 0.0f; float sum = 0.0f; octaves = clamp(octaves, 0.0f, 16.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_1d(fscale * p); sum += t * amp; - amp *= 0.5f; + maxamp += amp; + amp *= clamp(roughness, 0.0f, 1.0f); fscale *= 2.0f; } float rmd = octaves - floorf(octaves); if (rmd != 0.0f) { float t = noise_1d(fscale * p); float sum2 = sum + t * amp; - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0f - rmd) * sum + rmd * sum2; } else { - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */ -ccl_device_noinline float fractal_noise_2d(float2 p, float octaves) +ccl_device_noinline float fractal_noise_2d(float2 p, float octaves, float roughness) { float fscale = 1.0f; float amp = 1.0f; + float maxamp = 0.0f; float sum = 0.0f; octaves = clamp(octaves, 0.0f, 16.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_2d(fscale * p); sum += t * amp; - amp *= 0.5f; + maxamp += amp; + amp *= clamp(roughness, 0.0f, 1.0f); fscale *= 2.0f; } float rmd = octaves - floorf(octaves); if (rmd != 0.0f) { float t = noise_2d(fscale * p); float sum2 = sum + t * amp; - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0f - rmd) * sum + rmd * sum2; } else { - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */ -ccl_device_noinline float fractal_noise_3d(float3 p, float octaves) +ccl_device_noinline float fractal_noise_3d(float3 p, float octaves, float roughness) { float fscale = 1.0f; float amp = 1.0f; + float maxamp = 0.0f; float sum = 0.0f; octaves = clamp(octaves, 0.0f, 16.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_3d(fscale * p); sum += t * amp; - amp *= 0.5f; + maxamp += amp; + amp *= clamp(roughness, 0.0f, 1.0f); fscale *= 2.0f; } float rmd = octaves - floorf(octaves); if (rmd != 0.0f) { float t = noise_3d(fscale * p); float sum2 = sum + t * amp; - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0f - rmd) * sum + rmd * sum2; } else { - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */ -ccl_device_noinline float fractal_noise_4d(float4 p, float octaves) +ccl_device_noinline float fractal_noise_4d(float4 p, float octaves, float roughness) { float fscale = 1.0f; float amp = 1.0f; + float maxamp = 0.0f; float sum = 0.0f; octaves = clamp(octaves, 0.0f, 16.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_4d(fscale * p); sum += t * amp; - amp *= 0.5f; + maxamp += amp; + amp *= clamp(roughness, 0.0f, 1.0f); fscale *= 2.0f; } float rmd = octaves - floorf(octaves); if (rmd != 0.0f) { float t = noise_4d(fscale * p); float sum2 = sum + t * amp; - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - sum2 *= ((float)(1 << (n + 1)) / (float)((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0f - rmd) * sum + rmd * sum2; } else { - sum *= ((float)(1 << n) / (float)((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/svm_noisetex.h index 12884c6cb25..920dd7d9d02 100644 --- a/intern/cycles/kernel/svm/svm_noisetex.h +++ b/intern/cycles/kernel/svm/svm_noisetex.h @@ -50,24 +50,34 @@ ccl_device_inline float4 random_float4_offset(float seed) 100.0f + hash_float2_to_float(make_float2(seed, 3.0f)) * 100.0f); } -ccl_device void noise_texture_1d( - float co, float detail, float distortion, bool color_is_needed, float *value, float3 *color) +ccl_device void noise_texture_1d(float co, + float detail, + float roughness, + float distortion, + bool color_is_needed, + float *value, + float3 *color) { float p = co; if (distortion != 0.0f) { p += snoise_1d(p + random_float_offset(0.0f)) * distortion; } - *value = fractal_noise_1d(p, detail); + *value = fractal_noise_1d(p, detail, roughness); if (color_is_needed) { *color = make_float3(*value, - fractal_noise_1d(p + random_float_offset(1.0f), detail), - fractal_noise_1d(p + random_float_offset(2.0f), detail)); + fractal_noise_1d(p + random_float_offset(1.0f), detail, roughness), + fractal_noise_1d(p + random_float_offset(2.0f), detail, roughness)); } } -ccl_device void noise_texture_2d( - float2 co, float detail, float distortion, bool color_is_needed, float *value, float3 *color) +ccl_device void noise_texture_2d(float2 co, + float detail, + float roughness, + float distortion, + bool color_is_needed, + float *value, + float3 *color) { float2 p = co; if (distortion != 0.0f) { @@ -75,16 +85,21 @@ ccl_device void noise_texture_2d( snoise_2d(p + random_float2_offset(1.0f)) * distortion); } - *value = fractal_noise_2d(p, detail); + *value = fractal_noise_2d(p, detail, roughness); if (color_is_needed) { *color = make_float3(*value, - fractal_noise_2d(p + random_float2_offset(2.0f), detail), - fractal_noise_2d(p + random_float2_offset(3.0f), detail)); + fractal_noise_2d(p + random_float2_offset(2.0f), detail, roughness), + fractal_noise_2d(p + random_float2_offset(3.0f), detail, roughness)); } } -ccl_device void noise_texture_3d( - float3 co, float detail, float distortion, bool color_is_needed, float *value, float3 *color) +ccl_device void noise_texture_3d(float3 co, + float detail, + float roughness, + float distortion, + bool color_is_needed, + float *value, + float3 *color) { float3 p = co; if (distortion != 0.0f) { @@ -93,16 +108,21 @@ ccl_device void noise_texture_3d( snoise_3d(p + random_float3_offset(2.0f)) * distortion); } - *value = fractal_noise_3d(p, detail); + *value = fractal_noise_3d(p, detail, roughness); if (color_is_needed) { *color = make_float3(*value, - fractal_noise_3d(p + random_float3_offset(3.0f), detail), - fractal_noise_3d(p + random_float3_offset(4.0f), detail)); + fractal_noise_3d(p + random_float3_offset(3.0f), detail, roughness), + fractal_noise_3d(p + random_float3_offset(4.0f), detail, roughness)); } } -ccl_device void noise_texture_4d( - float4 co, float detail, float distortion, bool color_is_needed, float *value, float3 *color) +ccl_device void noise_texture_4d(float4 co, + float detail, + float roughness, + float distortion, + bool color_is_needed, + float *value, + float3 *color) { float4 p = co; if (distortion != 0.0f) { @@ -112,11 +132,11 @@ ccl_device void noise_texture_4d( snoise_4d(p + random_float4_offset(3.0f)) * distortion); } - *value = fractal_noise_4d(p, detail); + *value = fractal_noise_4d(p, detail, roughness); if (color_is_needed) { *color = make_float3(*value, - fractal_noise_4d(p + random_float4_offset(4.0f), detail), - fractal_noise_4d(p + random_float4_offset(5.0f), detail)); + fractal_noise_4d(p + random_float4_offset(4.0f), detail, roughness), + fractal_noise_4d(p + random_float4_offset(5.0f), detail, roughness)); } } @@ -128,21 +148,27 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg, uint offsets2, int *offset) { - uint vector_stack_offset, w_stack_offset, scale_stack_offset, detail_stack_offset; - uint distortion_stack_offset, value_stack_offset, color_stack_offset; + uint vector_stack_offset, w_stack_offset, scale_stack_offset; + uint detail_stack_offset, roughness_stack_offset, distortion_stack_offset; + uint value_stack_offset, color_stack_offset; svm_unpack_node_uchar4( offsets1, &vector_stack_offset, &w_stack_offset, &scale_stack_offset, &detail_stack_offset); - svm_unpack_node_uchar3( - offsets2, &distortion_stack_offset, &value_stack_offset, &color_stack_offset); + svm_unpack_node_uchar4(offsets2, + &roughness_stack_offset, + &distortion_stack_offset, + &value_stack_offset, + &color_stack_offset); - uint4 defaults = read_node(kg, offset); + uint4 defaults1 = read_node(kg, offset); + uint4 defaults2 = read_node(kg, offset); float3 vector = stack_load_float3(stack, vector_stack_offset); - float w = stack_load_float_default(stack, w_stack_offset, defaults.x); - float scale = stack_load_float_default(stack, scale_stack_offset, defaults.y); - float detail = stack_load_float_default(stack, detail_stack_offset, defaults.z); - float distortion = stack_load_float_default(stack, distortion_stack_offset, defaults.w); + float w = stack_load_float_default(stack, w_stack_offset, defaults1.x); + float scale = stack_load_float_default(stack, scale_stack_offset, defaults1.y); + float detail = stack_load_float_default(stack, detail_stack_offset, defaults1.z); + float roughness = stack_load_float_default(stack, roughness_stack_offset, defaults1.w); + float distortion = stack_load_float_default(stack, distortion_stack_offset, defaults2.x); vector *= scale; w *= scale; @@ -151,11 +177,13 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg, float3 color; switch (dimensions) { case 1: - noise_texture_1d(w, detail, distortion, stack_valid(color_stack_offset), &value, &color); + noise_texture_1d( + w, detail, roughness, distortion, stack_valid(color_stack_offset), &value, &color); break; case 2: noise_texture_2d(make_float2(vector.x, vector.y), detail, + roughness, distortion, stack_valid(color_stack_offset), &value, @@ -163,11 +191,12 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg, break; case 3: noise_texture_3d( - vector, detail, distortion, stack_valid(color_stack_offset), &value, &color); + vector, detail, roughness, distortion, stack_valid(color_stack_offset), &value, &color); break; case 4: noise_texture_4d(make_float4(vector.x, vector.y, vector.z, w), detail, + roughness, distortion, stack_valid(color_stack_offset), &value, diff --git a/intern/cycles/kernel/svm/svm_wave.h b/intern/cycles/kernel/svm/svm_wave.h index 64102535f7d..c4763475b47 100644 --- a/intern/cycles/kernel/svm/svm_wave.h +++ b/intern/cycles/kernel/svm/svm_wave.h @@ -23,9 +23,10 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type, NodeWaveRingsDirection rings_dir, NodeWaveProfile profile, float3 p, - float detail, float distortion, + float detail, float dscale, + float droughness, float phase) { /* Prevent precision issues on unit coordinates. */ @@ -66,7 +67,7 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type, n += phase; if (distortion != 0.0f) - n += distortion * (fractal_noise_3d(p * dscale, detail) * 2.0f - 1.0f); + n += distortion * (fractal_noise_3d(p * dscale, detail, droughness) * 2.0f - 1.0f); if (profile == NODE_WAVE_PROFILE_SIN) { return 0.5f + 0.5f * sinf(n - M_PI_2_F); @@ -84,35 +85,40 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type, ccl_device void svm_node_tex_wave( KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) { - uint4 defaults1 = read_node(kg, offset); - uint4 defaults2 = read_node(kg, offset); + uint4 node2 = read_node(kg, offset); + uint4 node3 = read_node(kg, offset); /* RNA properties */ uint type_offset, bands_dir_offset, rings_dir_offset, profile_offset; /* Inputs, Outputs */ - uint co_offset, scale_offset, distortion_offset, detail_offset, dscale_offset, phase_offset; + uint co_offset, scale_offset, distortion_offset, detail_offset, dscale_offset, droughness_offset, + phase_offset; uint color_offset, fac_offset; svm_unpack_node_uchar4( node.y, &type_offset, &bands_dir_offset, &rings_dir_offset, &profile_offset); - svm_unpack_node_uchar4(node.z, &co_offset, &scale_offset, &distortion_offset, &detail_offset); - svm_unpack_node_uchar4(node.w, &dscale_offset, &phase_offset, &color_offset, &fac_offset); + svm_unpack_node_uchar3(node.z, &co_offset, &scale_offset, &distortion_offset); + svm_unpack_node_uchar4( + node.w, &detail_offset, &dscale_offset, &droughness_offset, &phase_offset); + svm_unpack_node_uchar2(node2.x, &color_offset, &fac_offset); float3 co = stack_load_float3(stack, co_offset); - float scale = stack_load_float_default(stack, scale_offset, defaults1.x); - float detail = stack_load_float_default(stack, detail_offset, defaults1.y); - float distortion = stack_load_float_default(stack, distortion_offset, defaults1.z); - float dscale = stack_load_float_default(stack, dscale_offset, defaults1.w); - float phase = stack_load_float_default(stack, phase_offset, defaults2.x); + float scale = stack_load_float_default(stack, scale_offset, node2.y); + float distortion = stack_load_float_default(stack, distortion_offset, node2.z); + float detail = stack_load_float_default(stack, detail_offset, node2.w); + float dscale = stack_load_float_default(stack, dscale_offset, node3.x); + float droughness = stack_load_float_default(stack, droughness_offset, node3.y); + float phase = stack_load_float_default(stack, phase_offset, node3.z); float f = svm_wave((NodeWaveType)type_offset, (NodeWaveBandsDirection)bands_dir_offset, (NodeWaveRingsDirection)rings_dir_offset, (NodeWaveProfile)profile_offset, co * scale, - detail, distortion, + detail, dscale, + droughness, phase); if (stack_valid(fac_offset)) diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index ac07d91c4ca..4b4958fe3da 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -922,6 +922,7 @@ NODE_DEFINE(NoiseTextureNode) SOCKET_IN_FLOAT(w, "W", 0.0f); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_FLOAT(detail, "Detail", 2.0f); + SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(distortion, "Distortion", 0.0f); SOCKET_OUT_FLOAT(fac, "Fac"); @@ -940,6 +941,7 @@ void NoiseTextureNode::compile(SVMCompiler &compiler) ShaderInput *w_in = input("W"); ShaderInput *scale_in = input("Scale"); ShaderInput *detail_in = input("Detail"); + ShaderInput *roughness_in = input("Roughness"); ShaderInput *distortion_in = input("Distortion"); ShaderOutput *fac_out = output("Fac"); ShaderOutput *color_out = output("Color"); @@ -948,6 +950,7 @@ void NoiseTextureNode::compile(SVMCompiler &compiler) int w_stack_offset = compiler.stack_assign_if_linked(w_in); int scale_stack_offset = compiler.stack_assign_if_linked(scale_in); int detail_stack_offset = compiler.stack_assign_if_linked(detail_in); + int roughness_stack_offset = compiler.stack_assign_if_linked(roughness_in); int distortion_stack_offset = compiler.stack_assign_if_linked(distortion_in); int fac_stack_offset = compiler.stack_assign_if_linked(fac_out); int color_stack_offset = compiler.stack_assign_if_linked(color_out); @@ -957,11 +960,13 @@ void NoiseTextureNode::compile(SVMCompiler &compiler) dimensions, compiler.encode_uchar4( vector_stack_offset, w_stack_offset, scale_stack_offset, detail_stack_offset), - compiler.encode_uchar4(distortion_stack_offset, fac_stack_offset, color_stack_offset)); - compiler.add_node(__float_as_int(w), - __float_as_int(scale), - __float_as_int(detail), - __float_as_int(distortion)); + compiler.encode_uchar4( + roughness_stack_offset, distortion_stack_offset, fac_stack_offset, color_stack_offset)); + compiler.add_node( + __float_as_int(w), __float_as_int(scale), __float_as_int(detail), __float_as_int(roughness)); + + compiler.add_node( + __float_as_int(distortion), SVM_STACK_INVALID, SVM_STACK_INVALID, SVM_STACK_INVALID); tex_mapping.compile_end(compiler, vector_in, vector_stack_offset); } @@ -1343,14 +1348,14 @@ NODE_DEFINE(WaveTextureNode) profile_enum.insert("tri", NODE_WAVE_PROFILE_TRI); SOCKET_ENUM(profile, "Profile", profile_enum, NODE_WAVE_PROFILE_SIN); + SOCKET_IN_POINT( + vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_FLOAT(distortion, "Distortion", 0.0f); SOCKET_IN_FLOAT(detail, "Detail", 2.0f); SOCKET_IN_FLOAT(detail_scale, "Detail Scale", 0.0f); + SOCKET_IN_FLOAT(detail_roughness, "Detail Roughness", 0.5f); SOCKET_IN_FLOAT(phase, "Phase Offset", 0.0f); - SOCKET_IN_POINT( - vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); - SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); @@ -1368,6 +1373,7 @@ void WaveTextureNode::compile(SVMCompiler &compiler) ShaderInput *distortion_in = input("Distortion"); ShaderInput *detail_in = input("Detail"); ShaderInput *dscale_in = input("Detail Scale"); + ShaderInput *droughness_in = input("Detail Roughness"); ShaderInput *phase_in = input("Phase Offset"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); @@ -1378,20 +1384,22 @@ void WaveTextureNode::compile(SVMCompiler &compiler) compiler.encode_uchar4(type, bands_direction, rings_direction, profile), compiler.encode_uchar4(vector_offset, compiler.stack_assign_if_linked(scale_in), - compiler.stack_assign_if_linked(distortion_in), - compiler.stack_assign_if_linked(detail_in)), - compiler.encode_uchar4(compiler.stack_assign_if_linked(dscale_in), - compiler.stack_assign_if_linked(phase_in), - compiler.stack_assign_if_linked(color_out), - compiler.stack_assign_if_linked(fac_out))); + compiler.stack_assign_if_linked(distortion_in)), + compiler.encode_uchar4(compiler.stack_assign_if_linked(detail_in), + compiler.stack_assign_if_linked(dscale_in), + compiler.stack_assign_if_linked(droughness_in), + compiler.stack_assign_if_linked(phase_in))); - compiler.add_node(__float_as_int(scale), - __float_as_int(detail), + compiler.add_node(compiler.encode_uchar4(compiler.stack_assign_if_linked(color_out), + compiler.stack_assign_if_linked(fac_out)), + __float_as_int(scale), __float_as_int(distortion), - __float_as_int(detail_scale)); + __float_as_int(detail)); - compiler.add_node( - __float_as_int(phase), SVM_STACK_INVALID, SVM_STACK_INVALID, SVM_STACK_INVALID); + compiler.add_node(__float_as_int(detail_scale), + __float_as_int(detail_roughness), + __float_as_int(phase), + SVM_STACK_INVALID); tex_mapping.compile_end(compiler, vector_in, vector_offset); } diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index e201118574b..8316fa3cf9b 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -230,7 +230,7 @@ class NoiseTextureNode : public TextureNode { SHADER_NODE_CLASS(NoiseTextureNode) int dimensions; - float w, scale, detail, distortion; + float w, scale, detail, roughness, distortion; float3 vector; }; @@ -291,7 +291,7 @@ class WaveTextureNode : public TextureNode { NodeWaveRingsDirection rings_direction; NodeWaveProfile profile; - float scale, distortion, detail, detail_scale, phase; + float scale, distortion, detail, detail_scale, detail_roughness, phase; float3 vector; }; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl index 701b07b4aae..f25691c1a83 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl @@ -1,111 +1,115 @@ /* The fractal_noise functions are all exactly the same except for the input type. */ -float fractal_noise(float p, float octaves) +float fractal_noise(float p, float octaves, float roughness) { float fscale = 1.0; float amp = 1.0; + float maxamp = 0.0; float sum = 0.0; octaves = clamp(octaves, 0.0, 16.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); sum += t * amp; - amp *= 0.5; + maxamp += amp; + amp *= clamp(roughness, 0.0, 1.0); fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { float t = noise(fscale * p); float sum2 = sum + t * amp; - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); - sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise functions are all exactly the same except for the input type. */ -float fractal_noise(vec2 p, float octaves) +float fractal_noise(vec2 p, float octaves, float roughness) { float fscale = 1.0; float amp = 1.0; + float maxamp = 0.0; float sum = 0.0; octaves = clamp(octaves, 0.0, 16.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); sum += t * amp; - amp *= 0.5; + maxamp += amp; + amp *= clamp(roughness, 0.0, 1.0); fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { float t = noise(fscale * p); float sum2 = sum + t * amp; - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); - sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise functions are all exactly the same except for the input type. */ -float fractal_noise(vec3 p, float octaves) +float fractal_noise(vec3 p, float octaves, float roughness) { float fscale = 1.0; float amp = 1.0; + float maxamp = 0.0; float sum = 0.0; octaves = clamp(octaves, 0.0, 16.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); sum += t * amp; - amp *= 0.5; + maxamp += amp; + amp *= clamp(roughness, 0.0, 1.0); fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { float t = noise(fscale * p); float sum2 = sum + t * amp; - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); - sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } /* The fractal_noise functions are all exactly the same except for the input type. */ -float fractal_noise(vec4 p, float octaves) +float fractal_noise(vec4 p, float octaves, float roughness) { float fscale = 1.0; float amp = 1.0; + float maxamp = 0.0; float sum = 0.0; octaves = clamp(octaves, 0.0, 16.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); sum += t * amp; - amp *= 0.5; + maxamp += amp; + amp *= clamp(roughness, 0.0, 1.0); fscale *= 2.0; } float rmd = octaves - floor(octaves); if (rmd != 0.0) { float t = noise(fscale * p); float sum2 = sum + t * amp; - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); - sum2 *= (float(1 << (n + 1)) / float((1 << (n + 2)) - 1)); + sum /= maxamp; + sum2 /= maxamp + amp; return (1.0 - rmd) * sum + rmd * sum2; } else { - sum *= (float(1 << n) / float((1 << (n + 1)) - 1)); - return sum; + return sum / maxamp; } } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl index 6aeb23b1f99..d8d9ecdf287 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl @@ -32,23 +32,35 @@ vec4 random_vec4_offset(float seed) 100.0 + hash_vec2_to_float(vec2(seed, 3.0)) * 100.0); } -void node_noise_texture_1d( - vec3 co, float w, float scale, float detail, float distortion, out float value, out vec4 color) +void node_noise_texture_1d(vec3 co, + float w, + float scale, + float detail, + float roughness, + float distortion, + out float value, + out vec4 color) { float p = w * scale; if (distortion != 0.0) { p += snoise(p + random_float_offset(0.0)) * distortion; } - value = fractal_noise(p, detail); + value = fractal_noise(p, detail, roughness); color = vec4(value, - fractal_noise(p + random_float_offset(1.0), detail), - fractal_noise(p + random_float_offset(2.0), detail), + fractal_noise(p + random_float_offset(1.0), detail, roughness), + fractal_noise(p + random_float_offset(2.0), detail, roughness), 1.0); } -void node_noise_texture_2d( - vec3 co, float w, float scale, float detail, float distortion, out float value, out vec4 color) +void node_noise_texture_2d(vec3 co, + float w, + float scale, + float detail, + float roughness, + float distortion, + out float value, + out vec4 color) { vec2 p = co.xy * scale; if (distortion != 0.0) { @@ -56,15 +68,21 @@ void node_noise_texture_2d( snoise(p + random_vec2_offset(1.0)) * distortion); } - value = fractal_noise(p, detail); + value = fractal_noise(p, detail, roughness); color = vec4(value, - fractal_noise(p + random_vec2_offset(2.0), detail), - fractal_noise(p + random_vec2_offset(3.0), detail), + fractal_noise(p + random_vec2_offset(2.0), detail, roughness), + fractal_noise(p + random_vec2_offset(3.0), detail, roughness), 1.0); } -void node_noise_texture_3d( - vec3 co, float w, float scale, float detail, float distortion, out float value, out vec4 color) +void node_noise_texture_3d(vec3 co, + float w, + float scale, + float detail, + float roughness, + float distortion, + out float value, + out vec4 color) { vec3 p = co * scale; if (distortion != 0.0) { @@ -73,15 +91,21 @@ void node_noise_texture_3d( snoise(p + random_vec3_offset(2.0)) * distortion); } - value = fractal_noise(p, detail); + value = fractal_noise(p, detail, roughness); color = vec4(value, - fractal_noise(p + random_vec3_offset(3.0), detail), - fractal_noise(p + random_vec3_offset(4.0), detail), + fractal_noise(p + random_vec3_offset(3.0), detail, roughness), + fractal_noise(p + random_vec3_offset(4.0), detail, roughness), 1.0); } -void node_noise_texture_4d( - vec3 co, float w, float scale, float detail, float distortion, out float value, out vec4 color) +void node_noise_texture_4d(vec3 co, + float w, + float scale, + float detail, + float roughness, + float distortion, + out float value, + out vec4 color) { vec4 p = vec4(co, w) * scale; if (distortion != 0.0) { @@ -91,9 +115,9 @@ void node_noise_texture_4d( snoise(p + random_vec4_offset(3.0)) * distortion); } - value = fractal_noise(p, detail); + value = fractal_noise(p, detail, roughness); color = vec4(value, - fractal_noise(p + random_vec4_offset(4.0), detail), - fractal_noise(p + random_vec4_offset(5.0), detail), + fractal_noise(p + random_vec4_offset(4.0), detail, roughness), + fractal_noise(p + random_vec4_offset(5.0), detail, roughness), 1.0); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl index c72f9717af3..070f42a5e30 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_wave.glsl @@ -2,6 +2,7 @@ float calc_wave(vec3 p, float distortion, float detail, float detail_scale, + float detail_roughness, float phase, int wave_type, int bands_dir, @@ -46,7 +47,7 @@ float calc_wave(vec3 p, n += phase; if (distortion != 0.0) { - n += distortion * (fractal_noise(p * detail_scale, detail) * 2.0 - 1.0); + n += distortion * (fractal_noise(p * detail_scale, detail, detail_roughness) * 2.0 - 1.0); } if (wave_profile == 0) { /* profile sin */ @@ -67,6 +68,7 @@ void node_tex_wave(vec3 co, float distortion, float detail, float detail_scale, + float detail_roughness, float phase, float wave_type, float bands_dir, @@ -80,6 +82,7 @@ void node_tex_wave(vec3 co, distortion, detail, detail_scale, + detail_roughness, phase, int(wave_type), int(bands_dir), diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c b/source/blender/nodes/shader/nodes/node_shader_tex_noise.c index 2205a1a86a3..7b67c2d1f2e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.c @@ -26,6 +26,7 @@ static bNodeSocketTemplate sh_node_tex_noise_in[] = { {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, + {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, {-1, ""}, }; diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c b/source/blender/nodes/shader/nodes/node_shader_tex_wave.c index 0b6cd7ee4db..bba568ed5b7 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.c @@ -27,6 +27,7 @@ static bNodeSocketTemplate sh_node_tex_wave_in[] = { {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, {SOCK_FLOAT, N_("Detail Scale"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + {SOCK_FLOAT, N_("Detail Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, N_("Phase Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, {-1, ""}, }; -- cgit v1.2.3 From d7273f087dbf351d8520ac974190f5da335be429 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 9 Apr 2020 15:29:49 -0500 Subject: Fix T75405: Crash when increasing text object bevel depth One fewer coordinate needs to be calculated when extrusion is zero to avoid corrupting the end of the memory chunk. Differential Revision: https://developer.blender.org/D7368 --- source/blender/blenkernel/intern/curve.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 5b463059f18..401014e0853 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1877,7 +1877,8 @@ void BKE_curve_bevel_make(Object *ob, ListBase *disp) } /* Don't duplicate the last back vertex. */ angle = (cu->ext1 == 0.0f && (cu->flag & CU_BACK)) ? dangle : 0; - for (a = 0; a < cu->bevresol + 2; a++) { + int front_len = (cu->ext1 == 0.0f) ? cu->bevresol + 1 : cu->bevresol + 2; + for (a = 0; a < front_len; a++) { fp[0] = 0.0; fp[1] = (float)(cosf(angle) * (cu->ext2)); fp[2] = (float)(sinf(angle) * (cu->ext2)) + cu->ext1; -- cgit v1.2.3 From d85e2beb679520def6dd7f13f5acb1ae795b0888 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 9 Apr 2020 17:56:36 -0300 Subject: Fix T75526: Color Management Look saved in 2.82a resets in 2.83 Missing versioning after `rB9f4b090eec39`. Differential Revision: https://developer.blender.org/D7388 --- source/blender/blenloader/intern/versioning_280.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index ff3aef4588f..5c99760dc9c 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4405,6 +4405,17 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (!MAIN_VERSION_ATLEAST(bmain, 283, 3)) { + /* Color Management Look. */ + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + ColorManagedViewSettings *view_settings; + view_settings = &scene->view_settings; + if (BLI_str_startswith(view_settings->look, "Filmic - ")) { + STRNCPY(view_settings->look, view_settings->look + strlen("Filmic - ")); + } + else if (BLI_str_startswith(view_settings->look, "Standard - ")) { + STRNCPY(view_settings->look, view_settings->look + strlen("Standard - ")); + } + } /* Sequencer Tool region */ do_versions_area_ensure_tool_region(bmain, SPACE_SEQ, RGN_FLAG_HIDDEN); -- cgit v1.2.3 From 5ebbd8f672175ed19fc67f3dd9d3841c8985e211 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 10 Apr 2020 08:20:10 +1000 Subject: Cleanup: comment bone cycling & simplify logic --- source/blender/editors/armature/armature_select.c | 54 +++++++++++++---------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 98f067af148..b946c19dbe5 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -741,10 +741,14 @@ cache_end: else { int bias_max = INT_MIN; - /* Track cycle variables. */ + /* Track Cycle Variables + * - Offset is always set to the active bone. + * - The object & bone indices subtracted by the 'offset.as_u32' value. + * Unsigned subtraction wrapping means we always select the next bone in the cycle. + */ struct { union { - uint32_t cmp; + uint32_t as_u32; struct { #ifdef __BIG_ENDIAN__ uint16_t ob; @@ -753,17 +757,19 @@ cache_end: uint16_t bone; uint16_t ob; #endif - } index; - } active, test, best; + }; + } offset, test, best; } cycle_order; if (use_cycle) { bArmature *arm = obedit_orig->data; int ob_index = obedit_orig->runtime.select_id & 0xFFFF; int bone_index = BLI_findindex(arm->edbo, ebone_active_orig); - cycle_order.active.index.ob = ob_index; - cycle_order.active.index.bone = bone_index; - cycle_order.best.cmp = 0xffffffff; + /* Offset from the current active bone, so we cycle onto the next. */ + cycle_order.offset.ob = ob_index; + cycle_order.offset.bone = bone_index; + /* The value of the active bone (with offset subtracted, a signal to always overwrite). */ + cycle_order.best.as_u32 = 0; } for (int i = 0; i < hits; i++) { @@ -823,25 +829,25 @@ cache_end: /* Cycle selected items (objects & bones). */ if (use_cycle) { - bool found = false; - cycle_order.test.index.ob = hitresult & 0xFFFF; - cycle_order.test.index.bone = (hitresult & ~BONESEL_ANY) >> 16; + cycle_order.test.ob = hitresult & 0xFFFF; + cycle_order.test.bone = (hitresult & ~BONESEL_ANY) >> 16; if (ebone == ebone_active_orig) { - BLI_assert(cycle_order.test.index.ob == cycle_order.active.index.ob); - BLI_assert(cycle_order.test.index.bone == cycle_order.active.index.bone); + BLI_assert(cycle_order.test.ob == cycle_order.offset.ob); + BLI_assert(cycle_order.test.bone == cycle_order.offset.bone); } - cycle_order.test.cmp -= cycle_order.active.cmp; - - if (cycle_order.test.cmp < cycle_order.best.cmp && ebone != ebone_active_orig) { - cycle_order.best.cmp = cycle_order.test.cmp; - found = true; - } - else if (ELEM(result_cycle.ebone, NULL, ebone_active_orig)) { - /* Let the active bone become selected, but don't set the cycle order. */ - found = true; - } - - if (found) { + /* Subtraction as a single value is needed to support cycling through bones + * from multiple objects. So once the last bone is selected, + * the bits for the bone index wrap into the object, + * causing the next object to be stepped onto. */ + cycle_order.test.as_u32 -= cycle_order.offset.as_u32; + + /* Even though this logic avoids stepping onto the active bone, + * always set the 'best' value for the first time. + * Otherwise ensure the value is the smallest it can be, + * relative to the active bone, as long as it's not the active bone. */ + if ((cycle_order.best.as_u32 == 0) || + (cycle_order.test.as_u32 && (cycle_order.test.as_u32 < cycle_order.best.as_u32))) { + cycle_order.best = cycle_order.test; result_cycle.hitresult = hitresult; result_cycle.base = base; result_cycle.ebone = ebone; -- cgit v1.2.3 From 1a3928f33c76074a8d59eeca2b58187774cd7b4e Mon Sep 17 00:00:00 2001 From: Nicholas Rishel Date: Thu, 9 Apr 2020 16:43:09 -0600 Subject: Fix T75546: Solve possible endless loop in wintab initialisation Some Wintab drivers report a zero length queue, this causes an unplanned never ending loop. Differential Revision: https://developer.blender.org/D7392 Reviewed by: Ray Molenkamp --- intern/ghost/intern/GHOST_WindowWin32.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index d4fe7af8861..1ca0dd47cbe 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -1072,11 +1072,10 @@ void GHOST_WindowWin32::initializeWintab() // Wintab provides no way to determine the maximum queue size aside from checking if attempts // to change the queue size are successful. const int maxQueue = 500; - int initialQueueSize = m_wintab.queueSizeGet(m_wintab.context); - int queueSize = initialQueueSize; + int queueSize = m_wintab.queueSizeGet(m_wintab.context); while (queueSize < maxQueue) { - int testSize = min(queueSize + initialQueueSize, maxQueue); + int testSize = min(queueSize + 16, maxQueue); if (m_wintab.queueSizeSet(m_wintab.context, testSize)) { queueSize = testSize; } -- cgit v1.2.3 From 337a7ed292638542f702f1ce2119dc73072f0b56 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Thu, 9 Apr 2020 23:31:36 +0200 Subject: Pointclouds: fix point drawing The radius component is only one float. This resulted in only a third of intended points to draw and could lead to glitches. Pointcloud drawing will still change a lot in the future, this is just to be able to work on some simple tools. Differential Revision: https://developer.blender.org/D7390 --- source/blender/draw/intern/draw_cache_impl_pointcloud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c index 83757cb714a..53939b35285 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c @@ -134,7 +134,7 @@ static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache * /* initialize vertex format */ pos_id = GPU_vertformat_attr_add(&format, "pointcloud_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); radius_id = GPU_vertformat_attr_add( - &format, "pointcloud_radius", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + &format, "pointcloud_radius", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); } GPU_VERTBUF_DISCARD_SAFE(cache->pos); -- cgit v1.2.3 From d3cda49d143c455ae5c1093f3a5ce448c7de6ed3 Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Mon, 6 Apr 2020 14:49:29 +0200 Subject: Fix T74657: Grease Pencil Proportional Editing does not work for 'Individual Origins' Note gpencil doesnt do anything fancy like meshes in editmesh_islands_info_calc(), but it looks like there is actually no harm in allowing proportional editing with individual origins & gpencil editmode. Maniphest Tasks: T74657 Differential Revision: https://developer.blender.org/D7351 --- source/blender/editors/transform/transform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index c1e890ed5f1..d0866968daa 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -77,7 +77,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[3]); bool transdata_check_local_islands(TransInfo *t, short around) { - return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH)))); + return ((around == V3D_AROUND_LOCAL_ORIGINS) && ((ELEM(t->obedit_type, OB_MESH, OB_GPENCIL)))); } /* ************************** SPACE DEPENDENT CODE **************************** */ -- cgit v1.2.3 From da48a8ef506c3fc0778dba5c570bc44f975f9f21 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 10 Apr 2020 10:52:18 +0200 Subject: Fix T74815: Shapekeys animation is blocked after second append of the same object. Logic to handle shepkeys datablocks in helper in 'make local' code that checks which ID should be copied, and which can be directly made local, was wrong. --- source/blender/blenkernel/intern/lib_id.c | 34 ++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 487ec0bf161..9e4f1dda682 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1720,21 +1720,31 @@ static void library_make_local_copying_check(ID *id, /* Used_to_user stores ID pointer, not pointer to ID pointer. */ ID *par_id = (ID *)entry->id_pointer; - /* Our oh-so-beloved 'from' pointers... */ + /* Our oh-so-beloved 'from' pointers... Those should always be ignored here, since the actual + * relation we want to check is in the other way around. */ if (entry->usage_flag & IDWALK_CB_LOOPBACK) { - /* We totally disregard Object->proxy_from 'usage' here, - * this one would only generate fake positives. */ - if (GS(par_id->name) == ID_OB) { - BLI_assert(((Object *)par_id)->proxy_from == (Object *)id); - continue; +#ifndef NDEBUG + /* Some debug checks to ensure we explicitely are aware of all 'loopback' cases, since those + * may not always be manageable in the same way... */ + switch (GS(par_id->name)) { + case ID_OB: + BLI_assert(((Object *)par_id)->proxy_from == (Object *)id); + break; + case ID_KE: + BLI_assert(((Key *)par_id)->from == id); + break; + default: + BLI_assert(0); } +#endif + continue; + } - /* Shapekeys are considered 'private' to their owner ID here, and never tagged - * (since they cannot be linked), so we have to switch effective parent to their owner. - */ - if (GS(par_id->name) == ID_KE) { - par_id = ((Key *)par_id)->from; - } + /* Shapekeys are considered 'private' to their owner ID here, and never tagged + * (since they cannot be linked), so we have to switch effective parent to their owner. + */ + if (GS(par_id->name) == ID_KE) { + par_id = ((Key *)par_id)->from; } if (par_id->lib == NULL) { -- cgit v1.2.3 From 03dda57678d18d7a922c57cdd8a6e528389edbeb Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 10 Apr 2020 10:49:18 +0200 Subject: Fix T75141: Default template versioning error for grease pencil The versioning was done only for template because the code was after a return and never was executed for default scene. --- .../blenloader/intern/versioning_defaults.c | 172 ++++++++++----------- 1 file changed, 86 insertions(+), 86 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index f4288b1d174..19606b92479 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -376,6 +376,92 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) BLO_update_defaults_workspace(workspace, app_template); } + /* New grease pencil brushes and vertex paint setup. */ + { + /* Update Grease Pencil brushes. */ + Brush *brush; + + /* Pencil brush. */ + rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil"); + + /* Pen brush. */ + rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen"); + + /* Pen Soft brush. */ + brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft"); + if (brush) { + brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; + } + + /* Ink Pen brush. */ + rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen"); + + /* Ink Pen Rough brush. */ + rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); + + /* Marker Bold brush. */ + rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold"); + + /* Marker Chisel brush. */ + rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel"); + + /* Remove useless Fill Area.001 brush. */ + brush = BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2); + if (brush) { + BKE_id_delete(bmain, brush); + } + + /* Rename and fix materials and enable default object lights on. */ + if (app_template && STREQ(app_template, "2D_Animation")) { + Material *ma = NULL; + rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke"); + rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke"); + rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill"); + rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke"); + + /* Dots Stroke. */ + ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); + if (ma == NULL) { + ma = BKE_gpencil_material_add(bmain, "Dots Stroke"); + } + ma->gp_style->mode = GP_MATERIAL_MODE_DOT; + + /* Squares Stroke. */ + ma = BLI_findstring(&bmain->materials, "Squares Stroke", offsetof(ID, name) + 2); + if (ma == NULL) { + ma = BKE_gpencil_material_add(bmain, "Squares Stroke"); + } + ma->gp_style->mode = GP_MATERIAL_MODE_SQUARE; + + /* Change Solid Fill settings. */ + ma = BLI_findstring(&bmain->materials, "Solid Fill", offsetof(ID, name) + 2); + if (ma != NULL) { + ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; + } + + Object *ob = BLI_findstring(&bmain->objects, "Stroke", offsetof(ID, name) + 2); + if (ob && ob->type == OB_GPENCIL) { + ob->dtx |= OB_USE_GPENCIL_LIGHTS; + } + } + + /* Reset all grease pencil brushes. */ + Scene *scene = bmain->scenes.first; + BKE_brush_gpencil_paint_presets(bmain, scene->toolsettings); + + /* Ensure new Paint modes. */ + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); + BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); + + /* Enable cursor. */ + GpPaint *gp_paint = scene->toolsettings->gp_paint; + gp_paint->paint.flags |= PAINT_SHOW_BRUSH; + + /* Ensure Palette by default. */ + BKE_gpencil_palette_ensure(bmain, scene); + } + /* For builtin templates only. */ if (!blo_is_builtin_template(app_template)) { return; @@ -598,90 +684,4 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) } } } - - /* New grease pencil brushes and vertex paint setup. */ - { - /* Update Grease Pencil brushes. */ - Brush *brush; - - /* Pencil brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Pencil", "Pencil"); - - /* Pen brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Pen", "Pen"); - - /* Pen Soft brush. */ - brush = (Brush *)rename_id_for_versioning(bmain, ID_BR, "Draw Soft", "Pencil Soft"); - if (brush) { - brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN; - } - - /* Ink Pen brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Ink", "Ink Pen"); - - /* Ink Pen Rough brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Noise", "Ink Pen Rough"); - - /* Marker Bold brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Marker", "Marker Bold"); - - /* Marker Chisel brush. */ - rename_id_for_versioning(bmain, ID_BR, "Draw Block", "Marker Chisel"); - - /* Remove useless Fill Area.001 brush. */ - brush = BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2); - if (brush) { - BKE_id_delete(bmain, brush); - } - - /* Rename and fix materials and enable default object lights on. */ - if (app_template && STREQ(app_template, "2D_Animation")) { - Material *ma = NULL; - rename_id_for_versioning(bmain, ID_MA, "Black", "Solid Stroke"); - rename_id_for_versioning(bmain, ID_MA, "Red", "Squares Stroke"); - rename_id_for_versioning(bmain, ID_MA, "Grey", "Solid Fill"); - rename_id_for_versioning(bmain, ID_MA, "Black Dots", "Dots Stroke"); - - /* Dots Stroke. */ - ma = BLI_findstring(&bmain->materials, "Dots Stroke", offsetof(ID, name) + 2); - if (ma == NULL) { - ma = BKE_gpencil_material_add(bmain, "Dots Stroke"); - } - ma->gp_style->mode = GP_MATERIAL_MODE_DOT; - - /* Squares Stroke. */ - ma = BLI_findstring(&bmain->materials, "Squares Stroke", offsetof(ID, name) + 2); - if (ma == NULL) { - ma = BKE_gpencil_material_add(bmain, "Squares Stroke"); - } - ma->gp_style->mode = GP_MATERIAL_MODE_SQUARE; - - /* Change Solid Fill settings. */ - ma = BLI_findstring(&bmain->materials, "Solid Fill", offsetof(ID, name) + 2); - if (ma != NULL) { - ma->gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; - } - - Object *ob = BLI_findstring(&bmain->objects, "Stroke", offsetof(ID, name) + 2); - if (ob && ob->type == OB_GPENCIL) { - ob->dtx |= OB_USE_GPENCIL_LIGHTS; - } - } - - /* Reset all grease pencil brushes. */ - Scene *scene = bmain->scenes.first; - BKE_brush_gpencil_paint_presets(bmain, scene->toolsettings); - - /* Ensure new Paint modes. */ - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_VERTEX_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_SCULPT_GPENCIL); - BKE_paint_ensure_from_paintmode(scene, PAINT_MODE_WEIGHT_GPENCIL); - - /* Enable cursor. */ - GpPaint *gp_paint = scene->toolsettings->gp_paint; - gp_paint->paint.flags |= PAINT_SHOW_BRUSH; - - /* Ensure Palette by default. */ - BKE_gpencil_palette_ensure(bmain, scene); - } } -- cgit v1.2.3 From 0b86943641ec59d7f1a0799c0f709e4f23d619f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Barschkis?= Date: Fri, 10 Apr 2020 12:10:13 +0200 Subject: Fix T74901: Smoke Simulation crashes on eevee and is not persistent on Cycles Issue was introduced in 5260aaf3b1c8 (fix for T73921). --- source/blender/draw/engines/eevee/eevee_lightcache.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index 9447c365c48..614c749b9aa 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -777,8 +777,6 @@ static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake) if (!lbake->resource_only) { BLI_mutex_unlock(lbake->mutex); } - - EEVEE_volumes_free_smoke_textures(); } /* Cache as in draw cache not light cache. */ @@ -1347,6 +1345,9 @@ void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float } eevee_lightbake_delete_resources(lbake); + + /* Free GPU smoke textures and the smoke domain list correctly: See also T73921.*/ + EEVEE_volumes_free_smoke_textures(); } /* This is to update the world irradiance and reflection contribution from -- cgit v1.2.3 From 9c5b0542069bfec5ba8bfa65fa031014d3750cbe Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 9 Apr 2020 21:00:11 +0200 Subject: Fix T71546: VSE stereoscopic strips issues with mismatched dimensions The issue is that the cachiing was adding the right view without the proper pre-processed buffer. D7389 --- source/blender/blenkernel/intern/sequencer.c | 67 +++++++++++++++++----------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 53d592326dd..1cc7f4afa49 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -106,6 +106,14 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ListBase *seqbasep, float cfra, int chanshown); +static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context, + Sequence *seq, + ImBuf *ibuf, + float cfra, + clock_t begin, + bool use_preprocess, + const bool is_proxy_image, + const bool is_preprocessed); static ImBuf *seq_render_strip(const SeqRenderData *context, SeqRenderState *state, Sequence *seq, @@ -3072,13 +3080,8 @@ static ImBuf *seq_render_image_strip(const SeqRenderData *context, BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[view_id], false); if (view_id != context->view_id) { - BKE_sequencer_cache_put(&localcontext, - seq, - cfra, - SEQ_CACHE_STORE_PREPROCESSED, - ibufs_arr[view_id], - 0, - false); + ibufs_arr[view_id] = seq_render_preprocess_ibuf( + &localcontext, seq, ibufs_arr[view_id], cfra, clock(), true, false, false); } } } @@ -3197,8 +3200,8 @@ static ImBuf *seq_render_movie_strip(const SeqRenderData *context, BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[view_id], false); } if (view_id != context->view_id) { - BKE_sequencer_cache_put( - &localcontext, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf_arr[view_id], 0, false); + ibuf_arr[view_id] = seq_render_preprocess_ibuf( + &localcontext, seq, ibuf_arr[view_id], cfra, clock(), true, false, false); } } @@ -3797,6 +3800,34 @@ static float seq_estimate_render_cost_end(Scene *scene, clock_t begin) } } +static ImBuf *seq_render_preprocess_ibuf(const SeqRenderData *context, + Sequence *seq, + ImBuf *ibuf, + float cfra, + clock_t begin, + bool use_preprocess, + const bool is_proxy_image, + const bool is_preprocessed) +{ + if (context->is_proxy_render == false && + (ibuf->x != context->rectx || ibuf->y != context->recty)) { + use_preprocess = true; + } + + if (use_preprocess) { + float cost = seq_estimate_render_cost_end(context->scene, begin); + BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false); + + /* Reset timer so we can get partial render time. */ + begin = seq_estimate_render_cost_begin(); + ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image, is_preprocessed); + } + + float cost = seq_estimate_render_cost_end(context->scene, begin); + BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf, cost, false); + return ibuf; +} + static ImBuf *seq_render_strip(const SeqRenderData *context, SeqRenderState *state, Sequence *seq, @@ -3845,22 +3876,8 @@ static ImBuf *seq_render_strip(const SeqRenderData *context, sequencer_imbuf_assign_spaces(context->scene, ibuf); } - if (context->is_proxy_render == false && - (ibuf->x != context->rectx || ibuf->y != context->recty)) { - use_preprocess = true; - } - - if (use_preprocess) { - float cost = seq_estimate_render_cost_end(context->scene, begin); - BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_RAW, ibuf, cost, false); - - /* reset timer so we can get partial render time */ - begin = seq_estimate_render_cost_begin(); - ibuf = input_preprocess(context, seq, cfra, ibuf, is_proxy_image, is_preprocessed); - } - - float cost = seq_estimate_render_cost_end(context->scene, begin); - BKE_sequencer_cache_put(context, seq, cfra, SEQ_CACHE_STORE_PREPROCESSED, ibuf, cost, false); + ibuf = seq_render_preprocess_ibuf( + context, seq, ibuf, cfra, begin, use_preprocess, is_proxy_image, is_preprocessed); } return ibuf; } -- cgit v1.2.3 From 9a7f5f1bb42292eeb70b4832e7883846df0cb76a Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Fri, 10 Apr 2020 13:58:56 +0200 Subject: Fix T67232: Multiples targetless IKs in a chain gives weird behaviour (known as FakeIK for FK posing) The issue was that the deps graph relation builder assumed that all bones that had a IK constraint on them would be evaluated. However for targetless IK bones, only the active bone would receive updates and the others would be skipped (as those would be treated as if the IK constraint was disabled). I didn't see an easy way to solve this from the depsgraph side of things. Instead I came up with a solution that I feel is quite strait forward and reflects what is actually supposed to happen under the hood. Now all targetless IK constraints are treated as disabled and will not be added to any relations in the depsgraph. Instead, a temporary IK constraint will be created when the bone in question is transformed. This is basically activating "Auto IK" for the bone while transforming. Reviewed By: Sergey, Brecht Differential Revision: http://developer.blender.org/D7378 --- .../intern/builder/deg_builder_relations_rig.cc | 6 ++ .../editors/transform/transform_convert_armature.c | 66 +++++++++++++++------- 2 files changed, 52 insertions(+), 20 deletions(-) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc index 6fe9a3c2a00..d05385a7d7c 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc @@ -68,6 +68,12 @@ void DepsgraphRelationBuilder::build_ik_pose(Object *object, bConstraint *con, RootPChanMap *root_map) { + if ((con->flag & CONSTRAINT_DISABLE) != 0) { + /* Do not add disabled IK constraints to the relations. If these needs to be temporarly + * enabled, they will be added as temporary constraints during transform. */ + return; + } + bKinematicConstraint *data = (bKinematicConstraint *)con->data; /* Attach owner to IK Solver to. */ bPoseChannel *rootchan = BKE_armature_ik_solver_find_root(pchan, data); diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index e52bd15b0d5..481198ea88c 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -58,6 +58,34 @@ typedef struct BoneInitData { float zwidth; } BoneInitData; +static void add_temporary_ik_constraint(bPoseChannel *pchan, bKinematicConstraint *targetless_con) +{ + bConstraint *con = BKE_constraint_add_for_pose( + NULL, pchan, "TempConstraint", CONSTRAINT_TYPE_KINEMATIC); + + /* for draw, but also for detecting while pose solving */ + pchan->constflag |= (PCHAN_HAS_IK | PCHAN_HAS_TARGET); + + bKinematicConstraint *temp_con_data = con->data; + + if (targetless_con) { + /* if exists, use values from last targetless (but disabled) IK-constraint as base */ + *temp_con_data = *targetless_con; + } + else { + temp_con_data->flag = CONSTRAINT_IK_TIP; + } + + temp_con_data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS; +} + +static void update_deg_with_temporary_ik(Main *bmain, Object *ob) +{ + BIK_clear_data(ob->pose); + /* TODO(sergey): Consider doing partial update only. */ + DEG_relations_tag_update(bmain); +} + static void add_pose_transdata( TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td) { @@ -200,8 +228,16 @@ static void add_pose_transdata( } td->loc = data->grabtarget; copy_v3_v3(td->iloc, td->loc); + data->flag |= CONSTRAINT_IK_AUTO; + /* Add a temporary auto IK constraint here, as we will only temporarly active this targetless + * bone during transform. (Targetless IK constraints are treated as if they are disabled + * unless they are transformed) */ + add_temporary_ik_constraint(pchan, data); + Main *bmain = CTX_data_main(t->context); + update_deg_with_temporary_ik(bmain, ob); + /* only object matrix correction */ copy_m3_m3(td->mtx, omat); pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON); @@ -221,7 +257,8 @@ bKinematicConstraint *has_targetless_ik(bPoseChannel *pchan) bConstraint *con = pchan->constraints.first; for (; con; con = con->next) { - if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce != 0.0f)) { + if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->flag & CONSTRAINT_OFF) == 0 && + (con->enforce != 0.0f)) { bKinematicConstraint *data = con->data; if (data->tar == NULL) { @@ -278,8 +315,6 @@ static short pose_grab_with_ik_add(bPoseChannel *pchan) } } } - - return 0; } } @@ -289,20 +324,7 @@ static short pose_grab_with_ik_add(bPoseChannel *pchan) } } - con = BKE_constraint_add_for_pose(NULL, pchan, "TempConstraint", CONSTRAINT_TYPE_KINEMATIC); - - /* for draw, but also for detecting while pose solving */ - pchan->constflag |= (PCHAN_HAS_IK | PCHAN_HAS_TARGET); - - data = con->data; - if (targetless) { - /* if exists, use values from last targetless (but disabled) IK-constraint as base */ - *data = *targetless; - } - else { - data->flag = CONSTRAINT_IK_TIP; - } - data->flag |= CONSTRAINT_IK_TEMP | CONSTRAINT_IK_AUTO | CONSTRAINT_IK_POS; + add_temporary_ik_constraint(pchan, targetless); copy_v3_v3(data->grabtarget, pchan->pose_tail); /* watch-it! has to be 0 here, since we're still on the @@ -415,9 +437,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob) /* iTaSC needs clear for new IK constraints */ if (tot_ik) { - BIK_clear_data(ob->pose); - /* TODO(sergey): Consider doing partial update only. */ - DEG_relations_tag_update(bmain); + update_deg_with_temporary_ik(bmain, ob); } return (tot_ik) ? 1 : 0; @@ -585,6 +605,12 @@ void pose_transform_mirror_update(TransInfo *t, TransDataContainer *tc, Object * /* TODO(germano): Realitve Mirror support */ } data->flag |= CONSTRAINT_IK_AUTO; + /* Add a temporary auto IK constraint here, as we will only temporarly active this targetless + * bone during transform. (Targetless IK constraints are treated as if they are disabled + * unless they are transformed) */ + add_temporary_ik_constraint(pchan, data); + Main *bmain = CTX_data_main(t->context); + update_deg_with_temporary_ik(bmain, ob); } if (pid) { -- cgit v1.2.3 From ab8e7ffc6411da2bbd9cfb574b8dfcb86390c5ea Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 10 Apr 2020 11:21:55 -0300 Subject: Fix T75378: Crash on clicking in the ghost icon of an appended proxy object --- source/blender/editors/space_outliner/outliner_select.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 64d86293fb7..0971d3526ad 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -308,7 +308,7 @@ static eOLDrawState tree_element_set_active_object(bContext *C, bool recursive) { TreeStoreElem *tselem = TREESTORE(te); - TreeStoreElem *parent_tselem; + TreeStoreElem *parent_tselem = NULL; Scene *sce; Base *base; Object *ob = NULL; -- cgit v1.2.3 From 475d7d073add3d1f49867d1ab5c0a1b4857adab8 Mon Sep 17 00:00:00 2001 From: Howard Trickey Date: Fri, 10 Apr 2020 10:59:27 -0400 Subject: Fix T74800 Bevel modifier generates vertex group weight > 1.0. Due to floating point approximations, the weights for interpolating the mdeformvert layer could add up to a tiny bit more than 1.0. This was not a problem in practice, but the mesh validation routine used in regression tests was testing for this and therefore failing. Just changed interpolation of mdeformverts to clamp max to 1.0f. --- source/blender/blenkernel/intern/customdata.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index 754c2ce097f..eb5a90a7c4a 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -315,6 +315,9 @@ static void layerInterp_mdeformvert(const void **sources, if (totweight) { dvert->totweight = totweight; for (i = 0, node = dest_dwlink; node; node = node->next, i++) { + if (node->dw.weight > 1.0f) { + node->dw.weight = 1.0f; + } dvert->dw[i] = node->dw; } } -- cgit v1.2.3 From d216a0b505735da432448c438982b5cac1cba9ee Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 10 Apr 2020 17:28:26 +0200 Subject: Fix T75489: Crash on deleting current workspace from the outliner It should not be possible to delete the current workspace from the outliner. Show an error message instead. --- source/blender/editors/space_outliner/outliner_edit.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 60e6b423720..3ae100b6209 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -54,6 +54,7 @@ #include "BKE_outliner_treehash.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_workspace.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -473,6 +474,14 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto id->name); return; } + else if (te->idcode == ID_WS) { + BKE_workspace_id_tag_all_visible(bmain, LIB_TAG_DOIT); + if (id->tag & LIB_TAG_DOIT) { + BKE_reportf( + reports, RPT_WARNING, "Cannot delete currently visible workspace id '%s'", id->name); + return; + } + } BKE_id_delete(bmain, id); -- cgit v1.2.3 From d6cefef98f87ae8554050052d0fa4f965a2ce809 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 10 Apr 2020 20:07:11 +0200 Subject: UI: Better support for linked data-blocks in search buttons In RNA pointer search buttons (i.e. the ones with an eyedropper), data-blocks were handled badly. It was not possible to select a linked data-block that had the same name as a local one, which is especially common with library overrides. Neither was there a hint to tell appart linked data-blocks and which .blend file they come from. These issues are addressed now, we show an "L" prefix and the .blend file name in the search box (like in ID-templates). Changes here are quite simple, since the heavy lifting was already done through c20c203b8226. Addresses T73156. --- source/blender/editors/interface/interface_utils.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 2d687781b61..79a90d27373 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -36,6 +36,7 @@ #include "BLT_translation.h" +#include "BKE_lib_id.h" #include "BKE_report.h" #include "MEM_guardedalloc.h" @@ -395,11 +396,12 @@ void ui_rna_collection_search_cb(const struct bContext *C, uiSearchItems *items) { uiRNACollectionSearch *data = arg; - char *name; int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop); ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list"); CollItemSearch *cis; const bool skip_filter = data->search_but && !data->search_but->changed; + char name_buf[UI_MAX_DRAW_STR]; + char *name; /* build a temporary list of relevant items first */ RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) { @@ -417,24 +419,31 @@ void ui_rna_collection_search_cb(const struct bContext *C, } } - /* Could use the string length here. */ - name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); - iconid = 0; if (itemptr.type && RNA_struct_is_ID(itemptr.type)) { iconid = ui_id_icon_get(C, itemptr.data, false); + + BKE_id_full_name_ui_prefix_get(name_buf, itemptr.data); + BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI, + "Name string buffer should be big enough to hold full UI ID name"); + name = name_buf; + } + else { + name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), NULL); } if (name) { if (skip_filter || BLI_strcasestr(name, str)) { cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch"); cis->data = itemptr.data; - cis->name = MEM_dupallocN(name); + cis->name = BLI_strdup(name); cis->index = i; cis->iconid = iconid; BLI_addtail(items_list, cis); } - MEM_freeN(name); + if (name != name_buf) { + MEM_freeN(name); + } } i++; -- cgit v1.2.3 From 4f9a56cbc4e8719ee421a57fd6695bed633c591b Mon Sep 17 00:00:00 2001 From: Cody Winchester Date: Fri, 10 Apr 2020 14:03:36 +0200 Subject: Modifiers: Add Bone option for Texture Mask Object This patch adds the option to use an armature bone in place of an object for texture mask coordinates. This affects the 3 vertex weight modifiers, the displace modifier, the warp modifier, and the wave modifier. With minor changes from Bastien Montagne (@mont29). Differential Revision: https://developer.blender.org/D7348 --- .../startup/bl_ui/properties_data_modifier.py | 13 +++++++++++++ source/blender/makesdna/DNA_modifier_types.h | 10 ++++++++++ source/blender/makesrna/intern/rna_modifier.c | 10 ++++++++++ source/blender/modifiers/intern/MOD_displace.c | 10 ++++++++-- source/blender/modifiers/intern/MOD_util.c | 20 +++++++++++++++++--- source/blender/modifiers/intern/MOD_warp.c | 5 +++-- source/blender/modifiers/intern/MOD_wave.c | 9 ++++++++- source/blender/modifiers/intern/MOD_weightvg_util.c | 2 ++ source/blender/modifiers/intern/MOD_weightvg_util.h | 1 + source/blender/modifiers/intern/MOD_weightvgedit.c | 11 +++++++++-- source/blender/modifiers/intern/MOD_weightvgmix.c | 11 +++++++++-- .../blender/modifiers/intern/MOD_weightvgproximity.c | 11 +++++++++-- 12 files changed, 99 insertions(+), 14 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 5510941c4a5..61151b3e02b 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -396,6 +396,10 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): if md.texture_coords == 'OBJECT': col.label(text="Object:") col.prop(md, "texture_coords_object", text="") + obj = md.texture_coords_object + if obj and obj.type == 'ARMATURE': + col.label(text="Bone:") + col.prop_search(md, "texture_coords_bone", obj.data, "bones", text="") elif md.texture_coords == 'UV' and ob.type == 'MESH': col.label(text="UV Map:") col.prop_search(md, "uv_layer", ob.data, "uv_layers", text="") @@ -1244,6 +1248,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): if md.texture_coords == 'OBJECT': layout.prop(md, "texture_coords_object", text="Object") + obj = md.texture_coords_object + if obj and obj.type == 'ARMATURE': + layout.prop_search(md, "texture_coords_bone", obj.data, "bones", text="Bone") elif md.texture_coords == 'UV' and ob.type == 'MESH': layout.prop_search(md, "uv_layer", ob.data, "uv_layers") @@ -1296,6 +1303,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): layout.prop_search(md, "uv_layer", ob.data, "uv_layers") elif md.texture_coords == 'OBJECT': layout.prop(md, "texture_coords_object") + obj = md.texture_coords_object + if obj and obj.type == 'ARMATURE': + layout.prop_search(md, "texture_coords_bone", obj.data, "bones") layout.separator() @@ -1366,6 +1376,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): if md.mask_tex_mapping == 'OBJECT': layout.prop(md, "mask_tex_map_object", text="Object") + obj = md.mask_text_map_object + if obj and obj.type == 'ARMATURE': + layout.prop_search(md, "mask_tex_map_bone", obj.data, "bones", text="Bone") elif md.mask_tex_mapping == 'UV' and ob.type == 'MESH': layout.prop_search(md, "mask_tex_uv_layer", ob.data, "uv_layers") diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 855bf8434be..b2b2a73848a 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -138,6 +138,7 @@ typedef struct MappingInfoModifierData { struct Tex *texture; struct Object *map_object; + char map_bone[64]; /** MAX_CUSTOMDATA_LAYER_NAME. */ char uvlayer_name[64]; int uvlayer_tmp; @@ -496,6 +497,7 @@ typedef struct DisplaceModifierData { /* keep in sync with MappingInfoModifierData */ struct Tex *texture; struct Object *map_object; + char map_bone[64]; /** MAX_CUSTOMDATA_LAYER_NAME. */ char uvlayer_name[64]; int uvlayer_tmp; @@ -651,6 +653,7 @@ typedef struct WaveModifierData { /* keep in sync with MappingInfoModifierData */ struct Tex *texture; struct Object *map_object; + char map_bone[64]; /** MAX_CUSTOMDATA_LAYER_NAME. */ char uvlayer_name[64]; int uvlayer_tmp; @@ -1318,6 +1321,7 @@ typedef struct WarpModifierData { /* keep in sync with MappingInfoModifierData */ struct Tex *texture; struct Object *map_object; + char map_bone[64]; /** MAX_CUSTOMDATA_LAYER_NAME. */ char uvlayer_name[64]; int uvlayer_tmp; @@ -1393,6 +1397,8 @@ typedef struct WeightVGEditModifierData { struct Tex *mask_texture; /** Name of the map object. */ struct Object *mask_tex_map_obj; + /** Name of the map bone. */ + char mask_tex_map_bone[64]; /** How to map the texture (using MOD_DISP_MAP_* enums). */ int mask_tex_mapping; /** Name of the UV map. MAX_CUSTOMDATA_LAYER_NAME. */ @@ -1444,6 +1450,8 @@ typedef struct WeightVGMixModifierData { struct Tex *mask_texture; /** Name of the map object. */ struct Object *mask_tex_map_obj; + /** Name of the map bone. */ + char mask_tex_map_bone[64]; /** How to map the texture!. */ int mask_tex_mapping; /** Name of the UV map. MAX_CUSTOMDATA_LAYER_NAME. */ @@ -1518,6 +1526,8 @@ typedef struct WeightVGProximityModifierData { struct Tex *mask_texture; /** Name of the map object. */ struct Object *mask_tex_map_obj; + /** Name of the map bone. */ + char mask_tex_map_bone[64]; /** How to map the texture!. */ int mask_tex_mapping; /** Name of the UV Map. MAX_CUSTOMDATA_LAYER_NAME. */ diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 36db4e5b33a..50a049883a1 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1785,6 +1785,11 @@ static void rna_def_modifier_generic_map_info(StructRNA *srna) RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "texture_coords_bone", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "map_bone"); + RNA_def_property_ui_text(prop, "Texture Coordinate Bone", "Bone to set the texture coordinates"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); } static void rna_def_modifier_warp(BlenderRNA *brna) @@ -4792,6 +4797,11 @@ static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna), RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "mask_tex_map_bone", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "mask_tex_map_bone"); + RNA_def_property_ui_text(prop, "Texture Coordinate Bone", "Which bone to take texture coordinates from"); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); } static void rna_def_modifier_weightvgedit(BlenderRNA *brna) diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index a1b02e201d9..a03167c082f 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -135,9 +135,15 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte { DisplaceModifierData *dmd = (DisplaceModifierData *)md; if (dmd->map_object != NULL && dmd->texmapping == MOD_DISP_MAP_OBJECT) { + if (dmd->map_bone[0] && dmd->map_object->type == OB_ARMATURE) { + DEG_add_object_relation( + ctx->node, dmd->map_object, DEG_OB_COMP_EVAL_POSE, "Displace Modifier"); + } + else { + DEG_add_object_relation( + ctx->node, dmd->map_object, DEG_OB_COMP_TRANSFORM, "Displace Modifier"); + } DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier"); - DEG_add_object_relation( - ctx->node, dmd->map_object, DEG_OB_COMP_TRANSFORM, "Displace Modifier"); } if (dmd->texmapping == MOD_DISP_MAP_GLOBAL || (ELEM( diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index c0014a2c0cd..912b4a871a6 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -36,6 +36,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_action.h" /* BKE_pose_channel_find_name */ #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_image.h" @@ -81,12 +82,25 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd, const int numVerts = mesh->totvert; int i; int texmapping = dmd->texmapping; - float mapob_imat[4][4]; + float mapref_imat[4][4]; if (texmapping == MOD_DISP_MAP_OBJECT) { if (dmd->map_object != NULL) { Object *map_object = dmd->map_object; - invert_m4_m4(mapob_imat, map_object->obmat); + if (dmd->map_bone[0] != '\0') { + bPoseChannel *pchan = BKE_pose_channel_find_name(map_object->pose, dmd->map_bone); + if (pchan) { + float mat_bone_world[4][4]; + mul_m4_m4m4(mat_bone_world, map_object->obmat, pchan->pose_mat); + invert_m4_m4(mapref_imat, mat_bone_world); + } + else { + invert_m4_m4(mapref_imat, map_object->obmat); + } + } + else { + invert_m4_m4(mapref_imat, map_object->obmat); + } } else { /* if there is no map object, default to local */ texmapping = MOD_DISP_MAP_LOCAL; @@ -145,7 +159,7 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd, break; case MOD_DISP_MAP_OBJECT: mul_v3_m4v3(*r_texco, ob->obmat, cos != NULL ? *cos : mv->co); - mul_m4_v3(mapob_imat, *r_texco); + mul_m4_v3(mapref_imat, *r_texco); break; } if (cos != NULL) { diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index c3515578e42..732644411b0 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -176,8 +176,9 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) { - DEG_add_object_relation( - ctx->node, wmd->map_object, DEG_OB_COMP_TRANSFORM, "Warp Modifier map"); + warp_deps_object_bone_new(ctx->node, wmd->map_object, wmd->map_bone); + + DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier map"); } if (wmd->texture != NULL) { DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Warp Modifier"); diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 56b2a81fdb1..024d70bcfda 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -102,7 +102,14 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, wmd->objectcenter, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); } if (wmd->map_object != NULL) { - DEG_add_object_relation(ctx->node, wmd->map_object, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); + if (wmd->map_bone[0] && wmd->map_object->type == OB_ARMATURE) { + DEG_add_object_relation( + ctx->node, wmd->map_object, DEG_OB_COMP_EVAL_POSE, "Wave Modifier"); + } + else { + DEG_add_object_relation( + ctx->node, wmd->map_object, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); + } } if (wmd->objectcenter != NULL || wmd->map_object != NULL) { DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier"); diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.c b/source/blender/modifiers/intern/MOD_weightvg_util.c index 23e4da32ed7..633bbb1421b 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.c +++ b/source/blender/modifiers/intern/MOD_weightvg_util.c @@ -137,6 +137,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, const int tex_use_channel, const int tex_mapping, Object *tex_map_object, + const char *text_map_bone, const char *tex_uvlayer_name, const bool invert_vgroup_mask) { @@ -163,6 +164,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, */ t_map.texture = texture; t_map.map_object = tex_map_object; + BLI_strncpy(t_map.map_bone, text_map_bone, sizeof(t_map.map_bone)); BLI_strncpy(t_map.uvlayer_name, tex_uvlayer_name, sizeof(t_map.uvlayer_name)); t_map.texmapping = tex_mapping; diff --git a/source/blender/modifiers/intern/MOD_weightvg_util.h b/source/blender/modifiers/intern/MOD_weightvg_util.h index bcd1076eac6..6a43ac69fe9 100644 --- a/source/blender/modifiers/intern/MOD_weightvg_util.h +++ b/source/blender/modifiers/intern/MOD_weightvg_util.h @@ -73,6 +73,7 @@ void weightvg_do_mask(const ModifierEvalContext *ctx, const int tex_use_channel, const int tex_mapping, Object *tex_map_object, + const char *text_map_bone, const char *tex_uvlayer_name, const bool invert_vgroup_mask); diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index ba1745f7b5e..e304ca31e86 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -135,8 +135,14 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGEdit Modifier"); + if (wmd->mask_tex_map_bone[0] && wmd->mask_tex_map_obj->type == OB_ARMATURE) { + DEG_add_object_relation( + ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_EVAL_POSE, "WeightVGEdit Modifier"); + } + else { + DEG_add_object_relation( + ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGEdit Modifier"); + } DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier"); } else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) { @@ -261,6 +267,7 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes wmd->mask_tex_use_channel, wmd->mask_tex_mapping, wmd->mask_tex_map_obj, + wmd->mask_tex_map_bone, wmd->mask_tex_uvlayer_name, invert_vgroup_mask); diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 4984853d41e..b109d0af5aa 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -181,8 +181,14 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md; if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGMix Modifier"); + if (wmd->mask_tex_map_bone[0] && wmd->mask_tex_map_obj->type == OB_ARMATURE) { + DEG_add_object_relation( + ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_EVAL_POSE, "WeightVGMix Modifier"); + } + else { + DEG_add_object_relation( + ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGMix Modifier"); + } DEG_add_object_relation( ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_GEOMETRY, "WeightVGMix Modifier"); @@ -395,6 +401,7 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes wmd->mask_tex_use_channel, wmd->mask_tex_mapping, wmd->mask_tex_map_obj, + wmd->mask_tex_map_bone, wmd->mask_tex_uvlayer_name, invert_vgroup_mask); diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 7c9242ed900..2cee3878767 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -376,8 +376,14 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGProximity Modifier"); + if (wmd->mask_tex_map_bone[0] && wmd->mask_tex_map_obj->type == OB_ARMATURE) { + DEG_add_object_relation( + ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_EVAL_POSE, "WeightVGProximity Modifier"); + } + else { + DEG_add_object_relation( + ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGProximity Modifier"); + } DEG_add_object_relation( ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier"); } @@ -589,6 +595,7 @@ static Mesh *applyModifier(ModifierData *md, const ModifierEvalContext *ctx, Mes wmd->mask_tex_use_channel, wmd->mask_tex_mapping, wmd->mask_tex_map_obj, + wmd->mask_tex_map_bone, wmd->mask_tex_uvlayer_name, invert_vgroup_mask); -- cgit v1.2.3 From a8d139ca20024fb3b63152989968ba2fd7434eae Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 10 Apr 2020 14:07:15 -0700 Subject: Fix for T75595: File Browser and Windows Unicode Paths Fix for incorrect conversion to utf16 in BLI_file_attributes(). Differential Revision: https://developer.blender.org/D7398 Reviewed by Brecht Van Lommel --- source/blender/blenlib/intern/storage.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c index 9b437b02c0a..154fc966ca2 100644 --- a/source/blender/blenlib/intern/storage.c +++ b/source/blender/blenlib/intern/storage.c @@ -232,8 +232,9 @@ eFileAttributes BLI_file_attributes(const char *path) int ret = 0; # ifdef WIN32 - wchar_t wline[FILE_MAXDIR]; - BLI_strncpy_wchar_from_utf8(wline, path, ARRAY_SIZE(wline)); + WCHAR wline[FILE_MAXDIR]; + size_t bsize = count_utf_16_from_8(path); + conv_utf_8_to_16(path, wline, bsize); DWORD attr = GetFileAttributesW(wline); if (attr & FILE_ATTRIBUTE_READONLY) { ret |= FILE_ATTR_READONLY; -- cgit v1.2.3 From b0350d8310f253b01b57a5a46ca982cc787b9478 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 11 Apr 2020 16:42:01 +0200 Subject: Revert "GPUViewport: Use GPUBatch for viewport drawing" This reverts commit 862ec829422241878b3345661476d8551935aed2. It causes crashes on some systems, see T75584. --- source/blender/gpu/intern/gpu_viewport.c | 131 +++++++------------------------ 1 file changed, 28 insertions(+), 103 deletions(-) diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index fdbfa16a365..b2e1cb17946 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -65,24 +65,6 @@ typedef struct ViewportTempTexture { GPUTexture *texture; } ViewportTempTexture; -/* Struct storing a viewport specific GPUBatch. - * The end-goal is to have a single batch shared across viewport and use a model matrix to place - * the batch. Due to OCIO and Image/UV editor we are not able to use an model matrix yet. */ -struct GPUViewportBatch { - GPUBatch *batch; - struct { - rctf rect_pos; - rctf rect_uv; - } last_used_parameters; -} GPUViewportBatch; - -static struct { - GPUVertFormat format; - struct { - uint pos, tex_coord; - } attr_id; -} g_viewport = {{0}}; - struct GPUViewport { int size[2]; int flag; @@ -115,7 +97,6 @@ struct GPUViewport { /* TODO(fclem) the uvimage display use the viewport but do not set any view transform for the * moment. The end goal would be to let the GPUViewport do the color management. */ bool do_color_management; - struct GPUViewportBatch batch; }; enum { @@ -644,76 +625,6 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_framebuffer_restore(); } -/* -------------------------------------------------------------------- */ -/** \name Viewport Batches - * \{ */ - -static GPUVertFormat *gpu_viewport_batch_format(void) -{ - if (g_viewport.format.attr_len == 0) { - GPUVertFormat *format = &g_viewport.format; - g_viewport.attr_id.pos = GPU_vertformat_attr_add( - format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - g_viewport.attr_id.tex_coord = GPU_vertformat_attr_add( - format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - } - return &g_viewport.format; -} - -static GPUBatch *gpu_viewport_batch_create(const rctf *rect_pos, const rctf *rect_uv) -{ - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(gpu_viewport_batch_format()); - const uint vbo_len = 4; - GPU_vertbuf_data_alloc(vbo, vbo_len); - - GPUVertBufRaw pos_step, tex_coord_step; - GPU_vertbuf_attr_get_raw_data(vbo, g_viewport.attr_id.pos, &pos_step); - GPU_vertbuf_attr_get_raw_data(vbo, g_viewport.attr_id.tex_coord, &tex_coord_step); - - copy_v2_fl2(GPU_vertbuf_raw_step(&pos_step), rect_pos->xmin, rect_pos->ymin); - copy_v2_fl2(GPU_vertbuf_raw_step(&tex_coord_step), rect_uv->xmin, rect_uv->ymin); - copy_v2_fl2(GPU_vertbuf_raw_step(&pos_step), rect_pos->xmax, rect_pos->ymin); - copy_v2_fl2(GPU_vertbuf_raw_step(&tex_coord_step), rect_uv->xmax, rect_uv->ymin); - copy_v2_fl2(GPU_vertbuf_raw_step(&pos_step), rect_pos->xmin, rect_pos->ymax); - copy_v2_fl2(GPU_vertbuf_raw_step(&tex_coord_step), rect_uv->xmin, rect_uv->ymax); - copy_v2_fl2(GPU_vertbuf_raw_step(&pos_step), rect_pos->xmax, rect_pos->ymax); - copy_v2_fl2(GPU_vertbuf_raw_step(&tex_coord_step), rect_uv->xmax, rect_uv->ymax); - - return GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); -} - -static GPUBatch *gpu_viewport_batch_get(GPUViewport *viewport, - const rctf *rect_pos, - const rctf *rect_uv) -{ - const float compare_limit = 0.0001f; - const bool parameters_changed = - (!BLI_rctf_compare( - &viewport->batch.last_used_parameters.rect_pos, rect_pos, compare_limit) || - !BLI_rctf_compare(&viewport->batch.last_used_parameters.rect_uv, rect_uv, compare_limit)); - - if (viewport->batch.batch && parameters_changed) { - GPU_batch_discard(viewport->batch.batch); - viewport->batch.batch = NULL; - } - - if (!viewport->batch.batch) { - viewport->batch.batch = gpu_viewport_batch_create(rect_pos, rect_uv); - viewport->batch.last_used_parameters.rect_pos = *rect_pos; - viewport->batch.last_used_parameters.rect_uv = *rect_uv; - } - return viewport->batch.batch; -} - -static void gpu_viewport_batch_free(GPUViewport *viewport) -{ - if (viewport->batch.batch) { - GPU_batch_discard(viewport->batch.batch); - viewport->batch.batch = NULL; - } -} - -/** \} */ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, const rctf *rect_pos, @@ -724,6 +635,10 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, GPUTexture *color = dtxl->color; GPUTexture *color_overlay = dtxl->color_overlay; + GPUVertFormat *vert_format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint texco = GPU_vertformat_attr_add(vert_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + bool use_ocio = false; if (viewport->do_color_management && display_colorspace) { @@ -735,26 +650,38 @@ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, true); } - GPUBatch *batch = gpu_viewport_batch_get(viewport, rect_pos, rect_uv); - if (use_ocio) { - GPU_batch_program_set_imm_shader(batch); - } - else { - GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE); - GPU_batch_uniform_1i(batch, "display_transform", display_colorspace); - GPU_batch_uniform_1i(batch, "image_texture", 0); - GPU_batch_uniform_1i(batch, "overlays_texture", 1); + if (!use_ocio) { + immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE); + immUniform1i("display_transform", display_colorspace); + immUniform1i("image_texture", 0); + immUniform1i("overlays_texture", 1); } GPU_texture_bind(color, 0); GPU_texture_bind(color_overlay, 1); - GPU_batch_draw(batch); + + immBegin(GPU_PRIM_TRI_STRIP, 4); + + immAttr2f(texco, rect_uv->xmin, rect_uv->ymin); + immVertex2f(pos, rect_pos->xmin, rect_pos->ymin); + immAttr2f(texco, rect_uv->xmax, rect_uv->ymin); + immVertex2f(pos, rect_pos->xmax, rect_pos->ymin); + immAttr2f(texco, rect_uv->xmin, rect_uv->ymax); + immVertex2f(pos, rect_pos->xmin, rect_pos->ymax); + immAttr2f(texco, rect_uv->xmax, rect_uv->ymax); + immVertex2f(pos, rect_pos->xmax, rect_pos->ymax); + + immEnd(); + GPU_texture_unbind(color); GPU_texture_unbind(color_overlay); if (use_ocio) { IMB_colormanagement_finish_glsl_draw(); } + else { + immUnbindProgram(); + } } /** @@ -818,8 +745,8 @@ void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, * Merge and draw the buffers of \a viewport into the currently active framebuffer, performing * color transform to display space. * - * \param rect: Coordinates to draw into. By swapping min and max values, drawing can be done - * with inversed axis coordinates (upside down or sideways). + * \param rect: Coordinates to draw into. By swapping min and max values, drawing can be done with + * inversed axis coordinates (upside down or sideways). */ void GPU_viewport_draw_to_screen(GPUViewport *viewport, int view, const rcti *rect) { @@ -996,7 +923,5 @@ void GPU_viewport_free(GPUViewport *viewport) DRW_instance_data_list_free(viewport->idatalist); MEM_freeN(viewport->idatalist); - gpu_viewport_batch_free(viewport); - MEM_freeN(viewport); } -- cgit v1.2.3 From 5cc7036aa39506f3af3608ad450f1fdf6b2f3347 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Fri, 10 Apr 2020 22:00:21 +0200 Subject: Factorize some common modifiers depsgraph relation update code. Add a utility to deal with common 'object or posebone transform' case. --- source/blender/modifiers/intern/MOD_displace.c | 10 ++-------- source/blender/modifiers/intern/MOD_util.c | 16 ++++++++++++++++ source/blender/modifiers/intern/MOD_util.h | 4 ++++ source/blender/modifiers/intern/MOD_uvwarp.c | 20 ++++---------------- source/blender/modifiers/intern/MOD_warp.c | 21 ++++++--------------- source/blender/modifiers/intern/MOD_wave.c | 14 ++++---------- source/blender/modifiers/intern/MOD_weightvgedit.c | 11 +++-------- source/blender/modifiers/intern/MOD_weightvgmix.c | 11 +++-------- .../modifiers/intern/MOD_weightvgproximity.c | 10 ++-------- 9 files changed, 44 insertions(+), 73 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index a03167c082f..b074e398ab6 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -135,14 +135,8 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte { DisplaceModifierData *dmd = (DisplaceModifierData *)md; if (dmd->map_object != NULL && dmd->texmapping == MOD_DISP_MAP_OBJECT) { - if (dmd->map_bone[0] && dmd->map_object->type == OB_ARMATURE) { - DEG_add_object_relation( - ctx->node, dmd->map_object, DEG_OB_COMP_EVAL_POSE, "Displace Modifier"); - } - else { - DEG_add_object_relation( - ctx->node, dmd->map_object, DEG_OB_COMP_TRANSFORM, "Displace Modifier"); - } + MOD_depsgraph_update_object_bone_relation( + ctx->node, dmd->map_object, dmd->map_bone, "Displace Modifier"); DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier"); } if (dmd->texmapping == MOD_DISP_MAP_GLOBAL || diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 912b4a871a6..f6b7c829c13 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -262,6 +262,22 @@ void MOD_get_vgroup( } } +void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, + Object *object, + const char *bonename, + const char *description) +{ + if (object == NULL) { + return; + } + if (bonename[0] != '\0' && object->type == OB_ARMATURE) { + DEG_add_object_relation(node, object, DEG_OB_COMP_EVAL_POSE, description); + } + else { + DEG_add_object_relation(node, object, DEG_OB_COMP_TRANSFORM, description); + } +} + /* only called by BKE_modifier.h/modifier.c */ void modifier_type_init(ModifierTypeInfo *types[]) { diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index e1991de3bb8..38e2083082d 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -56,4 +56,8 @@ void MOD_get_vgroup(struct Object *ob, struct MDeformVert **dvert, int *defgrp_index); +void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node, + struct Object *object, + const char *bonename, + const char *description); #endif /* __MOD_UTIL_H__ */ diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index 46298c0e00d..6545159bdde 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -233,26 +233,14 @@ static void foreachObjectLink(ModifierData *md, Object *ob, ObjectWalkFunc walk, walk(userData, ob, &umd->object_src, IDWALK_CB_NOP); } -static void uv_warp_deps_object_bone_new(struct DepsNodeHandle *node, - Object *object, - const char *bonename) -{ - if (object != NULL) { - if (object->type == OB_ARMATURE && bonename[0]) { - DEG_add_object_relation(node, object, DEG_OB_COMP_EVAL_POSE, "UVWarp Modifier"); - } - else { - DEG_add_object_relation(node, object, DEG_OB_COMP_TRANSFORM, "UVWarp Modifier"); - } - } -} - static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { UVWarpModifierData *umd = (UVWarpModifierData *)md; - uv_warp_deps_object_bone_new(ctx->node, umd->object_src, umd->bone_src); - uv_warp_deps_object_bone_new(ctx->node, umd->object_dst, umd->bone_dst); + MOD_depsgraph_update_object_bone_relation( + ctx->node, umd->object_src, umd->bone_src, "UVWarp Modifier"); + MOD_depsgraph_update_object_bone_relation( + ctx->node, umd->object_dst, umd->bone_dst, "UVWarp Modifier"); DEG_add_modifier_to_transform_relation(ctx->node, "UVWarp Modifier"); } diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index 732644411b0..d5826342e07 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -152,31 +152,22 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void walk(userData, ob, md, "texture"); } -static void warp_deps_object_bone_new(struct DepsNodeHandle *node, - Object *object, - const char *bonename) -{ - if (bonename[0] && object->type == OB_ARMATURE) { - DEG_add_object_relation(node, object, DEG_OB_COMP_EVAL_POSE, "Warp Modifier"); - } - else { - DEG_add_object_relation(node, object, DEG_OB_COMP_TRANSFORM, "Warp Modifier"); - } -} - static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { WarpModifierData *wmd = (WarpModifierData *)md; if (wmd->object_from != NULL && wmd->object_to != NULL) { - warp_deps_object_bone_new(ctx->node, wmd->object_from, wmd->bone_from); - warp_deps_object_bone_new(ctx->node, wmd->object_to, wmd->bone_to); + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->object_from, wmd->bone_from, "Warp Modifier"); + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->object_to, wmd->bone_to, "Warp Modifier"); DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier"); } if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) { - warp_deps_object_bone_new(ctx->node, wmd->map_object, wmd->map_bone); + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->map_object, wmd->map_bone, "Warp Modifier"); DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier map"); } diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 024d70bcfda..02950870e8e 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -98,22 +98,16 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { WaveModifierData *wmd = (WaveModifierData *)md; + if (wmd->objectcenter != NULL) { DEG_add_object_relation(ctx->node, wmd->objectcenter, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); } - if (wmd->map_object != NULL) { - if (wmd->map_bone[0] && wmd->map_object->type == OB_ARMATURE) { - DEG_add_object_relation( - ctx->node, wmd->map_object, DEG_OB_COMP_EVAL_POSE, "Wave Modifier"); - } - else { - DEG_add_object_relation( - ctx->node, wmd->map_object, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); - } - } + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->map_object, wmd->map_bone, "Wave Modifier"); if (wmd->objectcenter != NULL || wmd->map_object != NULL) { DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier"); } + if (wmd->texture != NULL) { DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Wave Modifier"); } diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index e304ca31e86..c80a5f63903 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -45,6 +45,7 @@ #include "MEM_guardedalloc.h" #include "MOD_modifiertypes.h" +#include "MOD_util.h" #include "MOD_weightvg_util.h" /************************************** @@ -135,14 +136,8 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - if (wmd->mask_tex_map_bone[0] && wmd->mask_tex_map_obj->type == OB_ARMATURE) { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_EVAL_POSE, "WeightVGEdit Modifier"); - } - else { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGEdit Modifier"); - } + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGEdit Modifier"); DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier"); } else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) { diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index b109d0af5aa..e764b354ba5 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -43,6 +43,7 @@ #include "MEM_guardedalloc.h" #include "MOD_modifiertypes.h" +#include "MOD_util.h" #include "MOD_weightvg_util.h" /** @@ -181,14 +182,8 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md; if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - if (wmd->mask_tex_map_bone[0] && wmd->mask_tex_map_obj->type == OB_ARMATURE) { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_EVAL_POSE, "WeightVGMix Modifier"); - } - else { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGMix Modifier"); - } + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGMix Modifier"); DEG_add_object_relation( ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_GEOMETRY, "WeightVGMix Modifier"); diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 2cee3878767..7ef0d284ddf 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -376,14 +376,8 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - if (wmd->mask_tex_map_bone[0] && wmd->mask_tex_map_obj->type == OB_ARMATURE) { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_EVAL_POSE, "WeightVGProximity Modifier"); - } - else { - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_TRANSFORM, "WeightVGProximity Modifier"); - } + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGProximity Modifier"); DEG_add_object_relation( ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier"); } -- cgit v1.2.3 From 85de07e64c967e7d80e57d3df2db08fdec16794a Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Sat, 11 Apr 2020 17:12:56 +0200 Subject: Sanitize and cleanup a bit depsgraph relations building in some modifiers. This commit mainly: * Removes some uneeded dependencies to geometry of other objects (since we only use positions of those objects...). * Ensures `DEG_add_modifier_to_transform_relation` is only called once per modifier (in one case at least it could be called twice). * For modifiers using texture mask, only add dependencies to object used to generate texture coordinates when there is actually a texture set. No behavior change expected from this commit... --- source/blender/modifiers/intern/MOD_displace.c | 29 ++++++++++++++-------- source/blender/modifiers/intern/MOD_warp.c | 23 +++++++++++------ source/blender/modifiers/intern/MOD_wave.c | 20 +++++++++++---- source/blender/modifiers/intern/MOD_weightvgedit.c | 23 +++++++++++------ source/blender/modifiers/intern/MOD_weightvgmix.c | 24 ++++++++++-------- .../modifiers/intern/MOD_weightvgproximity.c | 24 ++++++++++++------ 6 files changed, 95 insertions(+), 48 deletions(-) diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index b074e398ab6..19a4e855153 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -134,19 +134,28 @@ static bool isDisabled(const struct Scene *UNUSED(scene), static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { DisplaceModifierData *dmd = (DisplaceModifierData *)md; - if (dmd->map_object != NULL && dmd->texmapping == MOD_DISP_MAP_OBJECT) { - MOD_depsgraph_update_object_bone_relation( - ctx->node, dmd->map_object, dmd->map_bone, "Displace Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier"); - } - if (dmd->texmapping == MOD_DISP_MAP_GLOBAL || - (ELEM( - dmd->direction, MOD_DISP_DIR_X, MOD_DISP_DIR_Y, MOD_DISP_DIR_Z, MOD_DISP_DIR_RGB_XYZ) && - dmd->space == MOD_DISP_SPACE_GLOBAL)) { - DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier"); + bool need_transform_relation = false; + + if (dmd->space == MOD_DISP_SPACE_GLOBAL && + ELEM(dmd->direction, MOD_DISP_DIR_X, MOD_DISP_DIR_Y, MOD_DISP_DIR_Z, MOD_DISP_DIR_RGB_XYZ)) { + need_transform_relation = true; } + if (dmd->texture != NULL) { DEG_add_generic_id_relation(ctx->node, &dmd->texture->id, "Displace Modifier"); + + if (dmd->map_object != NULL && dmd->texmapping == MOD_DISP_MAP_OBJECT) { + MOD_depsgraph_update_object_bone_relation( + ctx->node, dmd->map_object, dmd->map_bone, "Displace Modifier"); + need_transform_relation = true; + } + if (dmd->texmapping == MOD_DISP_MAP_GLOBAL) { + need_transform_relation = true; + } + } + + if (need_transform_relation) { + DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index d5826342e07..30d45d1fc65 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -155,24 +155,31 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { WarpModifierData *wmd = (WarpModifierData *)md; + bool need_transform_relation = false; if (wmd->object_from != NULL && wmd->object_to != NULL) { MOD_depsgraph_update_object_bone_relation( ctx->node, wmd->object_from, wmd->bone_from, "Warp Modifier"); MOD_depsgraph_update_object_bone_relation( ctx->node, wmd->object_to, wmd->bone_to, "Warp Modifier"); - - DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier"); + need_transform_relation = true; } - if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) { - MOD_depsgraph_update_object_bone_relation( - ctx->node, wmd->map_object, wmd->map_bone, "Warp Modifier"); - - DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier map"); - } if (wmd->texture != NULL) { DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Warp Modifier"); + + if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) { + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->map_object, wmd->map_bone, "Warp Modifier"); + need_transform_relation = true; + } + else if (wmd->texmapping == MOD_DISP_MAP_GLOBAL) { + need_transform_relation = true; + } + } + + if (need_transform_relation) { + DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index 02950870e8e..fd481018cc2 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -98,18 +98,28 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { WaveModifierData *wmd = (WaveModifierData *)md; + bool need_transform_relation = false; if (wmd->objectcenter != NULL) { DEG_add_object_relation(ctx->node, wmd->objectcenter, DEG_OB_COMP_TRANSFORM, "Wave Modifier"); - } - MOD_depsgraph_update_object_bone_relation( - ctx->node, wmd->map_object, wmd->map_bone, "Wave Modifier"); - if (wmd->objectcenter != NULL || wmd->map_object != NULL) { - DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier"); + need_transform_relation = true; } if (wmd->texture != NULL) { DEG_add_generic_id_relation(ctx->node, &wmd->texture->id, "Wave Modifier"); + + if ((wmd->texmapping == MOD_DISP_MAP_OBJECT) && wmd->map_object != NULL) { + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->map_object, wmd->map_bone, "Wave Modifier"); + need_transform_relation = true; + } + else if (wmd->texmapping == MOD_DISP_MAP_GLOBAL) { + need_transform_relation = true; + } + } + + if (need_transform_relation) { + DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index c80a5f63903..006191e29fc 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -135,16 +135,23 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md; - if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - MOD_depsgraph_update_object_bone_relation( - ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGEdit Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier"); - } - else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) { - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier"); - } + bool need_transform_relation = false; + if (wmd->mask_texture != NULL) { DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGEdit Modifier"); + + if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGEdit Modifier"); + need_transform_relation = true; + } + else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) { + need_transform_relation = true; + } + } + + if (need_transform_relation) { + DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index e764b354ba5..f256045f53a 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -181,19 +181,23 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md; - if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - MOD_depsgraph_update_object_bone_relation( - ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGMix Modifier"); - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_GEOMETRY, "WeightVGMix Modifier"); + bool need_transform_relation = false; - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier"); - } - else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) { - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier"); - } if (wmd->mask_texture != NULL) { DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGMix Modifier"); + + if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGMix Modifier"); + need_transform_relation = true; + } + else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) { + need_transform_relation = true; + } + } + + if (need_transform_relation) { + DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index 7ef0d284ddf..7b7aaaeb654 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -366,6 +366,8 @@ static void foreachTexLink(ModifierData *md, Object *ob, TexWalkFunc walk, void static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md; + bool need_transform_relation = false; + if (wmd->proximity_ob_target != NULL) { DEG_add_object_relation( ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_TRANSFORM, "WeightVGProximity Modifier"); @@ -374,17 +376,25 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation( ctx->node, wmd->proximity_ob_target, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier"); } + need_transform_relation = true; } - if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { - MOD_depsgraph_update_object_bone_relation( - ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGProximity Modifier"); - DEG_add_object_relation( - ctx->node, wmd->mask_tex_map_obj, DEG_OB_COMP_GEOMETRY, "WeightVGProximity Modifier"); - } + if (wmd->mask_texture != NULL) { DEG_add_generic_id_relation(ctx->node, &wmd->mask_texture->id, "WeightVGProximity Modifier"); + + if (wmd->mask_tex_map_obj != NULL && wmd->mask_tex_mapping == MOD_DISP_MAP_OBJECT) { + MOD_depsgraph_update_object_bone_relation( + ctx->node, wmd->mask_tex_map_obj, wmd->mask_tex_map_bone, "WeightVGProximity Modifier"); + need_transform_relation = true; + } + else if (wmd->mask_tex_mapping == MOD_DISP_MAP_GLOBAL) { + need_transform_relation = true; + } + } + + if (need_transform_relation) { + DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGProximity Modifier"); } - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGProximity Modifier"); } static bool isDisabled(const struct Scene *UNUSED(scene), -- cgit v1.2.3 From ddfec0851484fa735b60279743c892bf1952c538 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Sat, 11 Apr 2020 19:02:58 +0200 Subject: GPencil: Fix unreported missing update after removing stroke from python --- source/blender/makesrna/intern/rna_gpencil.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index 94814003e99..4a20df048bd 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -716,10 +716,13 @@ static bGPDstroke *rna_GPencil_stroke_new(bGPDframe *frame) return stroke; } -static void rna_GPencil_stroke_remove(bGPDframe *frame, +static void rna_GPencil_stroke_remove(ID *id, + bGPDframe *frame, ReportList *reports, PointerRNA *stroke_ptr) { + bGPdata *gpd = (bGPdata *)id; + bGPDstroke *stroke = stroke_ptr->data; if (BLI_findindex(&frame->strokes, stroke) == -1) { BKE_report(reports, RPT_ERROR, "Stroke not found in grease pencil frame"); @@ -729,7 +732,8 @@ static void rna_GPencil_stroke_remove(bGPDframe *frame, BLI_freelinkN(&frame->strokes, stroke); RNA_POINTER_INVALIDATE(stroke_ptr); - WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL); + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + WM_main_add_notifier(NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } static void rna_GPencil_stroke_close(ID *id, @@ -1282,7 +1286,7 @@ static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop) func = RNA_def_function(srna, "remove", "rna_GPencil_stroke_remove"); RNA_def_function_ui_description(func, "Remove a grease pencil stroke"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID); parm = RNA_def_pointer(func, "stroke", "GPencilStroke", "Stroke", "The stroke to remove"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0); -- cgit v1.2.3 From aeb42cf8ab2f9c8a7066748ff4889bc8a387dc0d Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Sat, 11 Apr 2020 12:59:21 -0600 Subject: Cycles/Optix: Support building the optix kernels on demand. CMake: `WITH_CYCLES_DEVICE_OPTIX` did not respect `WITH_CYCLES_CUDA_BINARIES` causing the optix kernel to be always build at build time. Code: `device_optix.cpp` did not count on the optix kernel not existing in the default location. For this to work, one should have before starting blender 1) working nvcc environment 2) Optix SDK installed and the OPTIX_ROOT_DIR environment variable pointing to it which is not set by default Differential Revision: https://developer.blender.org/D7400 Reviewed By: Brecht --- intern/cycles/device/device_optix.cpp | 8 +++++++- intern/cycles/kernel/CMakeLists.txt | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp index 42d7b00314c..37d6ae3d041 100644 --- a/intern/cycles/device/device_optix.cpp +++ b/intern/cycles/device/device_optix.cpp @@ -383,7 +383,13 @@ class OptiXDevice : public CUDADevice { { // Load and compile PTX module with OptiX kernels string ptx_data, ptx_filename = path_get("lib/kernel_optix.ptx"); - if (use_adaptive_compilation()) { + if (use_adaptive_compilation() || path_file_size(ptx_filename) == -1) { + if (!getenv("OPTIX_ROOT_DIR")) { + set_error( + "OPTIX_ROOT_DIR environment variable not set, must be set with the path to the " + "Optix SDK in order to compile the Optix kernel on demand."); + return false; + } ptx_filename = compile_kernel(requested_features, "kernel_optix", "optix", true); } if (ptx_filename.empty() || !path_read_text(ptx_filename, ptx_data)) { diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 3264b5afea2..ac3a85089c2 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -507,7 +507,7 @@ endif() # OptiX PTX modules -if(WITH_CYCLES_DEVICE_OPTIX) +if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) foreach(input ${SRC_OPTIX_KERNELS}) get_filename_component(input_we ${input} NAME_WE) @@ -677,7 +677,7 @@ source_group("svm" FILES ${SRC_SVM_HEADERS}) if(WITH_CYCLES_CUDA) add_dependencies(cycles_kernel cycles_kernel_cuda) endif() -if(WITH_CYCLES_DEVICE_OPTIX) +if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) add_dependencies(cycles_kernel cycles_kernel_optix) endif() -- cgit v1.2.3 From 0a747cd4e3a138942b6d85f9193ffc75aaa5fabf Mon Sep 17 00:00:00 2001 From: Ray Molenkamp Date: Sat, 11 Apr 2020 13:01:19 -0600 Subject: Cleanup: clang-format --- source/blender/makesrna/intern/rna_modifier.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 50a049883a1..176d8e6de91 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -4800,7 +4800,8 @@ static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna), prop = RNA_def_property(srna, "mask_tex_map_bone", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "mask_tex_map_bone"); - RNA_def_property_ui_text(prop, "Texture Coordinate Bone", "Which bone to take texture coordinates from"); + RNA_def_property_ui_text( + prop, "Texture Coordinate Bone", "Which bone to take texture coordinates from"); RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); } -- cgit v1.2.3 From f16fcb5bd5ecfc6ef80b537b77b9a7dd99d9ecf8 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 12 Apr 2020 02:37:31 +0200 Subject: Fix volume object not loading frame sequences correct in some cases Ensure we use the first frame as filepath so we can compute the number of leading zeros. For file validation, always test the first frame rather than the current scene frame. --- source/blender/editors/object/object_volume.c | 12 +++++++----- source/blender/editors/space_image/image_sequence.c | 9 ++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/object/object_volume.c b/source/blender/editors/object/object_volume.c index bb619972e80..b7da91b70ba 100644 --- a/source/blender/editors/object/object_volume.c +++ b/source/blender/editors/object/object_volume.c @@ -109,11 +109,6 @@ static int volume_import_exec(bContext *C, wmOperator *op) BLI_path_rel(volume->filepath, BKE_main_blendfile_path(bmain)); } - volume->is_sequence = (range->length > 1); - volume->frame_duration = (volume->is_sequence) ? range->length : 0; - volume->frame_start = 1; - volume->frame_offset = (volume->is_sequence) ? range->offset - 1 : 0; - if (!BKE_volume_load(volume, bmain)) { BKE_reportf(op->reports, RPT_WARNING, @@ -134,6 +129,13 @@ static int volume_import_exec(bContext *C, wmOperator *op) continue; } + /* Set sequence parameters after trying to load the first frame, for file validation we want + * to use a consistent frame rather than whatever corresponds to the current scene frame. */ + volume->is_sequence = (range->length > 1); + volume->frame_duration = (volume->is_sequence) ? range->length : 0; + volume->frame_start = 1; + volume->frame_offset = (volume->is_sequence) ? range->offset - 1 : 0; + if (BKE_volume_is_y_up(volume)) { object->rot[0] += M_PI_2; } diff --git a/source/blender/editors/space_image/image_sequence.c b/source/blender/editors/space_image/image_sequence.c index e65924d0eb9..0817bdda88d 100644 --- a/source/blender/editors/space_image/image_sequence.c +++ b/source/blender/editors/space_image/image_sequence.c @@ -64,6 +64,7 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges) char dir[FILE_MAXDIR]; const bool do_frame_range = RNA_boolean_get(op->ptr, "use_sequence_detection"); ImageFrameRange *range = NULL; + int range_first_frame = 0; RNA_string_get(op->ptr, "directory", dir); RNA_BEGIN (op->ptr, itemptr, "files") { @@ -79,7 +80,11 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges) /* still in the same sequence */ if (do_frame_range && (range != NULL) && (STREQLEN(base_head, head, FILE_MAX)) && (STREQLEN(base_tail, tail, FILE_MAX))) { - /* pass */ + /* Set filepath to first frame in the range. */ + if (frame->framenr < range_first_frame) { + BLI_join_dirfile(range->filepath, sizeof(range->filepath), dir, filename); + range_first_frame = frame->framenr; + } } else { /* start a new frame range */ @@ -89,6 +94,8 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges) BLI_strncpy(base_head, head, sizeof(base_head)); BLI_strncpy(base_tail, tail, sizeof(base_tail)); + + range_first_frame = frame->framenr; } BLI_addtail(&range->frames, frame); -- cgit v1.2.3 From e2003d9212575038ad79791fcdfab00199bf130f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 12 Apr 2020 15:44:06 +0200 Subject: UI: reorder adaptive sampling settings in order of importance --- intern/cycles/blender/addon/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 0ef813d9636..7cf615620a3 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -253,8 +253,8 @@ class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel): layout.active = cscene.use_adaptive_sampling col = layout.column(align=True) - col.prop(cscene, "adaptive_min_samples", text="Min Samples") col.prop(cscene, "adaptive_threshold", text="Noise Threshold") + col.prop(cscene, "adaptive_min_samples", text="Min Samples") class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): bl_label = "Advanced" -- cgit v1.2.3 From 5b79e0b80e49c1f75736a8b0bdeb8560a934b072 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sun, 12 Apr 2020 15:38:44 +0200 Subject: Fix volume object not rendering correct frame right after loading --- source/blender/editors/object/object_volume.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/object/object_volume.c b/source/blender/editors/object/object_volume.c index b7da91b70ba..3c1f7da2bd6 100644 --- a/source/blender/editors/object/object_volume.c +++ b/source/blender/editors/object/object_volume.c @@ -140,6 +140,8 @@ static int volume_import_exec(bContext *C, wmOperator *op) object->rot[0] += M_PI_2; } + BKE_volume_unload(volume); + imported = true; } BLI_freelistN(&ranges); -- cgit v1.2.3 From 77ca5ab6b02bbccc97d13c50d4174afe9471725a Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sun, 12 Apr 2020 22:09:46 +0200 Subject: Fix T74897: VSE animation doesn't work `seq_free_animdata()` removes fcurve pointers belonging to strips from `Scene` CoW datablock's `AnimData` during `BKE_scene_graph_update_for_newframe`. This causes problems with updating animation. This worked before rBbe2e41c397ba, because `AnimData` was freed by `BKE_animdata_free()` before `seq_free_animdata()` was executed, so it had no data to operate on and returned on precondition `if (scene->adt == NULL || scene->adt->action == NULL)` Reviewed By: mont29, brecht Maniphest Tasks: T74897 Differential Revision: https://developer.blender.org/D7264 --- source/blender/blenkernel/BKE_sequencer.h | 2 +- source/blender/blenkernel/intern/sequencer.c | 16 ++++++++++------ source/blender/editors/space_sequencer/sequencer_edit.c | 6 +++--- source/blender/makesrna/intern/rna_sequencer_api.c | 4 ++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 1b4ca1350ad..dd9414b81f7 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -265,7 +265,7 @@ void BKE_sequencer_base_clipboard_pointers_free(struct ListBase *seqbase); void BKE_sequencer_base_clipboard_pointers_store(struct Main *bmain, struct ListBase *seqbase); void BKE_sequencer_base_clipboard_pointers_restore(struct ListBase *seqbase, struct Main *bmain); -void BKE_sequence_free(struct Scene *scene, struct Sequence *seq); +void BKE_sequence_free(struct Scene *scene, struct Sequence *seq, const bool do_clean_animdata); void BKE_sequence_free_anim(struct Sequence *seq); const char *BKE_sequence_give_name(struct Sequence *seq); ListBase *BKE_sequence_seqbase_get(struct Sequence *seq, int *r_offset); diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 1cc7f4afa49..7d4e5e582c6 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -236,7 +236,8 @@ static void seq_free_strip(Strip *strip) static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cache, - const bool do_id_user) + const bool do_id_user, + const bool do_clean_animdata) { if (seq->strip) { seq_free_strip(seq->strip); @@ -271,7 +272,10 @@ static void BKE_sequence_free_ex(Scene *scene, BKE_sound_remove_scene_sound(scene, seq->scene_sound); } - seq_free_animdata(scene, seq); + /* XXX This must not be done in BKE code. */ + if (do_clean_animdata) { + seq_free_animdata(scene, seq); + } } if (seq->prop) { @@ -299,9 +303,9 @@ static void BKE_sequence_free_ex(Scene *scene, MEM_freeN(seq); } -void BKE_sequence_free(Scene *scene, Sequence *seq) +void BKE_sequence_free(Scene *scene, Sequence *seq, const bool do_clean_animdata) { - BKE_sequence_free_ex(scene, seq, true, true); + BKE_sequence_free_ex(scene, seq, true, true, do_clean_animdata); } /* Function to free imbuf and anim data on changes */ @@ -331,7 +335,7 @@ static void seq_free_sequence_recurse(Scene *scene, Sequence *seq, const bool do seq_free_sequence_recurse(scene, iseq, do_id_user); } - BKE_sequence_free_ex(scene, seq, false, do_id_user); + BKE_sequence_free_ex(scene, seq, false, do_id_user, true); } Editing *BKE_sequencer_editing_get(Scene *scene, bool alloc) @@ -501,7 +505,7 @@ void BKE_sequencer_editing_free(Scene *scene, const bool do_id_user) SEQ_BEGIN (ed, seq) { /* handle cache freeing above */ - BKE_sequence_free_ex(scene, seq, false, do_id_user); + BKE_sequence_free_ex(scene, seq, false, do_id_user, false); } SEQ_END; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index d34eed622d4..0175260a95c 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -720,7 +720,7 @@ static void recurs_del_seq_flag(Scene *scene, ListBase *lb, short flag, short de if (seq->type == SEQ_TYPE_META) { recurs_del_seq_flag(scene, &seq->seqbase, flag, 1); } - BKE_sequence_free(scene, seq); + BKE_sequence_free(scene, seq, true); } seq = seqn; } @@ -2641,7 +2641,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) } seq_next = seq->next; - BKE_sequence_free(scene, seq); + BKE_sequence_free(scene, seq, true); seq = seq_next; } else { @@ -2866,7 +2866,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) BLI_listbase_clear(&last_seq->seqbase); BLI_remlink(ed->seqbasep, last_seq); - BKE_sequence_free(scene, last_seq); + BKE_sequence_free(scene, last_seq, true); /* Empty meta strip, delete all effects depending on it. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index d49a2ed1ea0..6c8f51f97a1 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -193,7 +193,7 @@ static Sequence *rna_Sequences_new_image(ID *id, if (seq->strip->stripdata->name[0] == '\0') { BKE_report(reports, RPT_ERROR, "Sequences.new_image: unable to open image file"); BLI_remlink(&ed->seqbase, seq); - BKE_sequence_free(scene, seq); + BKE_sequence_free(scene, seq, true); return NULL; } @@ -382,7 +382,7 @@ static void rna_Sequences_remove( return; } - BKE_sequence_free(scene, seq); + BKE_sequence_free(scene, seq, true); RNA_POINTER_INVALIDATE(seq_ptr); DEG_relations_tag_update(bmain); -- cgit v1.2.3 From 5cabf1301a50906bdda391255696e8a85b6b555b Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sun, 12 Apr 2020 22:20:42 +0200 Subject: Fix T75382: VSE Strip Adjustments Not Immediately Visible Cache of effects wasn't invalidated on correct level. Add invalidation "rule" for invalidating downstream effects. Reviewed By: brecht Differential Revision: https://developer.blender.org/D7343 --- source/blender/blenkernel/intern/sequencer.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 7d4e5e582c6..7f89b946948 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -4166,8 +4166,16 @@ static void sequence_do_invalidate_dependent(Scene *scene, Sequence *seq, ListBa } if (BKE_sequence_check_depend(seq, cur)) { - BKE_sequencer_cache_cleanup_sequence( - scene, cur, seq, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT); + /* Effect must be invalidated completely if they depend on invalidated seq. */ + if ((cur->type & SEQ_TYPE_EFFECT) != 0) { + BKE_sequencer_cache_cleanup_sequence( + scene, cur, seq, SEQ_CACHE_ALL_TYPES); + } + else { + /* In case of alpha over for example only invalidate composite image */ + BKE_sequencer_cache_cleanup_sequence( + scene, cur, seq, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT); + } } if (cur->seqbase.first) { -- cgit v1.2.3 From 5081556bb517961f513e6874b4040ab8e623f1d7 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sun, 12 Apr 2020 22:27:28 +0200 Subject: Fix T75415: Changing text strip "start" leads to flickering image Cache must be invalidated before and after transformation, so all frames are properly invalidated. This also fixes wrong invalidated type, composite is enough here. Reviewed By: brecht Differential Revision: https://developer.blender.org/D7341 --- source/blender/makesrna/intern/rna_sequencer.c | 56 ++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index 04729f87cab..f486456bd38 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -291,7 +291,6 @@ static void rna_Sequence_frame_change_update(Main *bmain, Scene *UNUSED(scene), { Scene *scene = (Scene *)ptr->owner_id; do_sequence_frame_change_update(scene, (Sequence *)ptr->data); - rna_Sequence_invalidate_preprocessed_update(bmain, scene, ptr); } static void rna_Sequence_start_frame_set(PointerRNA *ptr, int value) @@ -299,8 +298,10 @@ static void rna_Sequence_start_frame_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; + BKE_sequence_invalidate_cache_composite(scene, seq); BKE_sequence_translate(scene, seq, value - seq->start); do_sequence_frame_change_update(scene, seq); + BKE_sequence_invalidate_cache_composite(scene, seq); } static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, int value) @@ -308,9 +309,11 @@ static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; + BKE_sequence_invalidate_cache_composite(scene, seq); BKE_sequence_tx_set_final_left(seq, value); BKE_sequence_single_fix(seq); do_sequence_frame_change_update(scene, seq); + BKE_sequence_invalidate_cache_composite(scene, seq); } static void rna_Sequence_end_frame_final_set(PointerRNA *ptr, int value) @@ -318,9 +321,47 @@ static void rna_Sequence_end_frame_final_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; + BKE_sequence_invalidate_cache_composite(scene, seq); BKE_sequence_tx_set_final_right(seq, value); BKE_sequence_single_fix(seq); do_sequence_frame_change_update(scene, seq); + BKE_sequence_invalidate_cache_composite(scene, seq); +} + +static void rna_Sequence_frame_offset_start_set(PointerRNA *ptr, int value) +{ + Sequence *seq = (Sequence *)ptr->data; + Scene *scene = (Scene *)ptr->owner_id; + + BKE_sequence_invalidate_cache_composite(scene, seq); + seq->startofs = value; +} + +static void rna_Sequence_frame_offset_end_set(PointerRNA *ptr, int value) +{ + Sequence *seq = (Sequence *)ptr->data; + Scene *scene = (Scene *)ptr->owner_id; + + BKE_sequence_invalidate_cache_composite(scene, seq); + seq->endofs = value; +} + +static void rna_Sequence_frame_still_start_set(PointerRNA *ptr, int value) +{ + Sequence *seq = (Sequence *)ptr->data; + Scene *scene = (Scene *)ptr->owner_id; + + BKE_sequence_invalidate_cache_composite(scene, seq); + seq->startstill = value; +} + +static void rna_Sequence_frame_still_end_set(PointerRNA *ptr, int value) +{ + Sequence *seq = (Sequence *)ptr->data; + Scene *scene = (Scene *)ptr->owner_id; + + BKE_sequence_invalidate_cache_composite(scene, seq); + seq->endstill = value; } static void rna_Sequence_anim_startofs_final_set(PointerRNA *ptr, int value) @@ -350,8 +391,10 @@ static void rna_Sequence_frame_length_set(PointerRNA *ptr, int value) Sequence *seq = (Sequence *)ptr->data; Scene *scene = (Scene *)ptr->owner_id; + BKE_sequence_invalidate_cache_composite(scene, seq); BKE_sequence_tx_set_final_right(seq, BKE_sequence_tx_get_final_left(seq, false) + value); do_sequence_frame_change_update(scene, seq); + BKE_sequence_invalidate_cache_composite(scene, seq); } static int rna_Sequence_frame_length_get(PointerRNA *ptr) @@ -374,6 +417,7 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value) Editing *ed = BKE_sequencer_editing_get(scene, false); ListBase *seqbase = BKE_sequence_seqbase(&ed->seqbase, seq); + BKE_sequence_invalidate_cache_composite(scene, seq); /* check channel increment or decrement */ const int channel_delta = (value >= seq->machine) ? 1 : -1; seq->machine = value; @@ -383,6 +427,7 @@ static void rna_Sequence_channel_set(PointerRNA *ptr, int value) BKE_sequence_base_shuffle_ex(seqbase, seq, scene, channel_delta); } BKE_sequencer_sort(scene); + BKE_sequence_invalidate_cache_composite(scene, seq); } static void rna_Sequence_frame_offset_range( @@ -1627,6 +1672,7 @@ static void rna_def_sequence(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); /* strip positioning */ + /* Cache has to be invalidated before and after transformation. */ prop = RNA_def_property(srna, "frame_final_duration", PROP_INT, PROP_TIME); RNA_def_property_range(prop, 1, MAXFRAME); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1684,14 +1730,16 @@ static void rna_def_sequence(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "startofs"); // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */ RNA_def_property_ui_text(prop, "Start Offset", ""); - RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Sequence_frame_offset_range"); + RNA_def_property_int_funcs( + prop, NULL, "rna_Sequence_frame_offset_start_set", "rna_Sequence_frame_offset_range"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update"); prop = RNA_def_property(srna, "frame_offset_end", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "endofs"); // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */ RNA_def_property_ui_text(prop, "End Offset", ""); - RNA_def_property_int_funcs(prop, NULL, NULL, "rna_Sequence_frame_offset_range"); + RNA_def_property_int_funcs( + prop, NULL, "rna_Sequence_frame_offset_end_set", "rna_Sequence_frame_offset_range"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update"); prop = RNA_def_property(srna, "frame_still_start", PROP_INT, PROP_TIME); @@ -1699,6 +1747,7 @@ static void rna_def_sequence(BlenderRNA *brna) // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */ RNA_def_property_range(prop, 0, MAXFRAME); RNA_def_property_ui_text(prop, "Start Still", ""); + RNA_def_property_int_funcs(prop, NULL, "rna_Sequence_frame_still_start_set", NULL); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update"); prop = RNA_def_property(srna, "frame_still_end", PROP_INT, PROP_TIME); @@ -1706,6 +1755,7 @@ static void rna_def_sequence(BlenderRNA *brna) // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* overlap tests */ RNA_def_property_range(prop, 0, MAXFRAME); RNA_def_property_ui_text(prop, "End Still", ""); + RNA_def_property_int_funcs(prop, NULL, "rna_Sequence_frame_still_end_set", NULL); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_frame_change_update"); prop = RNA_def_property(srna, "channel", PROP_INT, PROP_UNSIGNED); -- cgit v1.2.3 From bbf1c83370ea0682cafb99ad98e52ae625f360a9 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sun, 12 Apr 2020 22:42:31 +0200 Subject: Fix T74875: Preview shows previously cached frame after Hard Cut Add method to invalidate strip cache in range of non-overlapping strip. Invalidate original strip in range of new strip created by cutting. Reviewed By: brecht Differential Revision: https://developer.blender.org/D7313 --- source/blender/blenkernel/BKE_sequencer.h | 4 +++- source/blender/blenkernel/intern/seqcache.c | 15 +++++++++------ source/blender/blenkernel/intern/sequencer.c | 15 +++++++++++---- source/blender/editors/space_sequencer/sequencer_edit.c | 2 ++ 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index dd9414b81f7..cb95bacba4c 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -336,7 +336,8 @@ void BKE_sequencer_cache_cleanup(struct Scene *scene); void BKE_sequencer_cache_cleanup_sequence(struct Scene *scene, struct Sequence *seq, struct Sequence *seq_changed, - int invalidate_types); + int invalidate_types, + bool force_seq_changed_range); void BKE_sequencer_cache_iterate(struct Scene *scene, void *userdata, bool callback_init(void *userdata, size_t item_count), @@ -434,6 +435,7 @@ void BKE_sequence_invalidate_cache_composite(struct Scene *scene, struct Sequenc void BKE_sequence_invalidate_dependent(struct Scene *scene, struct Sequence *seq); void BKE_sequence_invalidate_scene_strips(struct Main *bmain, struct Scene *scene_target); void BKE_sequence_invalidate_movieclip_strips(struct Main *bmain, struct MovieClip *clip_target); +void BKE_sequence_invalidate_cache_in_range(struct Scene *scene, struct Sequence *seq, struct Sequence *range_mask, int invalidate_types); void BKE_sequencer_update_sound_bounds_all(struct Scene *scene); void BKE_sequencer_update_sound_bounds(struct Scene *scene, struct Sequence *seq); diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 12c5821e858..f999a98faac 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -1153,7 +1153,8 @@ void BKE_sequencer_cache_cleanup(Scene *scene) void BKE_sequencer_cache_cleanup_sequence(Scene *scene, Sequence *seq, Sequence *seq_changed, - int invalidate_types) + int invalidate_types, + bool force_seq_changed_range) { SeqCache *cache = seq_cache_get_from_scene(scene); if (!cache) { @@ -1169,12 +1170,14 @@ void BKE_sequencer_cache_cleanup_sequence(Scene *scene, int range_start = seq_changed->startdisp; int range_end = seq_changed->enddisp; - if (seq->startdisp > range_start) { - range_start = seq->startdisp; - } + if (!force_seq_changed_range) { + if (seq->startdisp > range_start) { + range_start = seq->startdisp; + } - if (seq->enddisp < range_end) { - range_end = seq->enddisp; + if (seq->enddisp < range_end) { + range_end = seq->enddisp; + } } int invalidate_composite = invalidate_types & SEQ_CACHE_STORE_FINAL_OUT; diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 7f89b946948..93e5a3bbd74 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -4168,13 +4168,12 @@ static void sequence_do_invalidate_dependent(Scene *scene, Sequence *seq, ListBa if (BKE_sequence_check_depend(seq, cur)) { /* Effect must be invalidated completely if they depend on invalidated seq. */ if ((cur->type & SEQ_TYPE_EFFECT) != 0) { - BKE_sequencer_cache_cleanup_sequence( - scene, cur, seq, SEQ_CACHE_ALL_TYPES); + BKE_sequencer_cache_cleanup_sequence(scene, cur, seq, SEQ_CACHE_ALL_TYPES, false); } else { /* In case of alpha over for example only invalidate composite image */ BKE_sequencer_cache_cleanup_sequence( - scene, cur, seq, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT); + scene, cur, seq, SEQ_CACHE_STORE_COMPOSITE | SEQ_CACHE_STORE_FINAL_OUT, false); } } @@ -4193,7 +4192,7 @@ static void sequence_invalidate_cache(Scene *scene, if (invalidate_self) { BKE_sequence_free_anim(seq); - BKE_sequencer_cache_cleanup_sequence(scene, seq, seq, invalidate_types); + BKE_sequencer_cache_cleanup_sequence(scene, seq, seq, invalidate_types, false); } if (seq->effectdata && seq->type == SEQ_TYPE_SPEED) { @@ -4205,6 +4204,14 @@ static void sequence_invalidate_cache(Scene *scene, BKE_sequencer_prefetch_stop(scene); } +void BKE_sequence_invalidate_cache_in_range(Scene *scene, + Sequence *seq, + Sequence *range_mask, + int invalidate_types) +{ + BKE_sequencer_cache_cleanup_sequence(scene, seq, range_mask, invalidate_types, true); +} + void BKE_sequence_invalidate_cache_raw(Scene *scene, Sequence *seq) { sequence_invalidate_cache(scene, seq, true, SEQ_CACHE_ALL_TYPES); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 0175260a95c..83671a0d600 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -838,6 +838,7 @@ static Sequence *split_seq_hard( BKE_sequence_reload_new_file(bmain, scene, seqn, false); BKE_sequence_calc(scene, seqn); + BKE_sequence_invalidate_cache_in_range(scene, seq, seqn, SEQ_CACHE_ALL_TYPES); } return seqn; } @@ -937,6 +938,7 @@ static Sequence *split_seq_soft( } BKE_sequence_calc(scene, seqn); + BKE_sequence_invalidate_cache_in_range(scene, seq, seqn, SEQ_CACHE_ALL_TYPES); } return seqn; } -- cgit v1.2.3 From 0c9e47705d287dc7ce976059b7eb0e7b463b2b42 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sun, 12 Apr 2020 22:57:34 +0200 Subject: Fix T75421: Wipe clock and Iris transition not working. Use enum items in RNA enum definition instead of hard-coded values. Broken by 5dcb6fb22f3 Cleanup: unused enums Reviewed By: brecht Differential Revision: https://developer.blender.org/D7342 --- source/blender/makesrna/intern/rna_sequencer.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index f486456bd38..b135c962891 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -2517,12 +2517,12 @@ static void rna_def_wipe(StructRNA *srna) PropertyRNA *prop; static const EnumPropertyItem wipe_type_items[] = { - {0, "SINGLE", 0, "Single", ""}, - {1, "DOUBLE", 0, "Double", ""}, - /* not used yet {2, "BOX", 0, "Box", ""}, */ - /* not used yet {3, "CROSS", 0, "Cross", ""}, */ - {4, "IRIS", 0, "Iris", ""}, - {5, "CLOCK", 0, "Clock", ""}, + {DO_SINGLE_WIPE, "SINGLE", 0, "Single", ""}, + {DO_DOUBLE_WIPE, "DOUBLE", 0, "Double", ""}, + /* not used yet {DO_BOX_WIPE, "BOX", 0, "Box", ""}, */ + /* not used yet {DO_CROSS_WIPE, "CROSS", 0, "Cross", ""}, */ + {DO_IRIS_WIPE, "IRIS", 0, "Iris", ""}, + {DO_CLOCK_WIPE, "CLOCK", 0, "Clock", ""}, {0, NULL, 0, NULL, NULL}, }; -- cgit v1.2.3 From c456671b53695e4ae7bc4871cb48835f4a7f1d3b Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Sun, 12 Apr 2020 23:56:03 +0200 Subject: Refactor sample operator Move sample operator functions to `ed_util_imbuf.c` and change common functions, so they can be used in image editor and sequencer. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D7315 --- source/blender/editors/include/ED_util_imbuf.h | 52 ++ source/blender/editors/space_image/image_ops.c | 389 +-------------- .../editors/space_sequencer/sequencer_view.c | 221 +------- source/blender/editors/util/CMakeLists.txt | 3 + source/blender/editors/util/ed_util_imbuf.c | 553 +++++++++++++++++++++ 5 files changed, 628 insertions(+), 590 deletions(-) create mode 100644 source/blender/editors/include/ED_util_imbuf.h create mode 100644 source/blender/editors/util/ed_util_imbuf.c diff --git a/source/blender/editors/include/ED_util_imbuf.h b/source/blender/editors/include/ED_util_imbuf.h new file mode 100644 index 00000000000..64349556744 --- /dev/null +++ b/source/blender/editors/include/ED_util_imbuf.h @@ -0,0 +1,52 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup editors + */ + +#ifndef __ED_UTIL_IMBUF_H__ +#define __ED_UTIL_IMBUF_H__ + +#include "BLI_compiler_attrs.h" + +#include "DNA_screen_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Main; +struct bContext; +struct wmOperator; +struct wmEvent; + +/* ed_util_imbuf.c */ +void ED_imbuf_sample_draw(const struct bContext *C, struct ARegion *region, void *arg_info); +void ED_imbuf_sample_exit(struct bContext *C, struct wmOperator *op); +int ED_imbuf_sample_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); +int ED_imbuf_sample_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); +void ED_imbuf_sample_cancel(struct bContext *C, struct wmOperator *op); +bool ED_imbuf_sample_poll(struct bContext *C); + +#ifdef __cplusplus +} +#endif + +#endif /* __ED_UTIL_IMBUF_H__ */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 14245327bdd..992727e3b11 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -91,6 +91,7 @@ #include "ED_screen.h" #include "ED_space_api.h" #include "ED_util.h" +#include "ED_util_imbuf.h" #include "ED_uvedit.h" #include "UI_interface.h" @@ -277,28 +278,6 @@ static bool space_image_main_area_not_uv_brush_poll(bContext *C) return 0; } -static bool image_sample_poll(bContext *C) -{ - SpaceImage *sima = CTX_wm_space_image(C); - if (sima == NULL) { - return false; - } - - Object *obedit = CTX_data_edit_object(C); - if (obedit) { - /* Disable when UV editing so it doesn't swallow all click events - * (use for setting cursor). */ - if (ED_space_image_show_uvedit(sima, obedit)) { - return false; - } - } - else if (sima->mode != SI_MODE_VIEW) { - return false; - } - - return true; -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -3099,85 +3078,6 @@ void IMAGE_OT_unpack(wmOperatorType *ot) /** \name Sample Image Operator * \{ */ -typedef struct ImageSampleInfo { - ARegionType *art; - void *draw_handle; - int x, y; - int channels; - - int width, height; - int sample_size; - - uchar col[4]; - float colf[4]; - float linearcol[4]; - int z; - float zf; - - uchar *colp; - const float *colfp; - int *zp; - float *zfp; - - bool draw; - bool color_manage; - int use_default_view; -} ImageSampleInfo; - -static void image_sample_draw(const bContext *C, ARegion *region, void *arg_info) -{ - ImageSampleInfo *info = arg_info; - if (!info->draw) { - return; - } - - Scene *scene = CTX_data_scene(C); - ED_image_draw_info(scene, - region, - info->color_manage, - info->use_default_view, - info->channels, - info->x, - info->y, - info->colp, - info->colfp, - info->linearcol, - info->zp, - info->zfp); - - if (info->sample_size > 1) { - const wmWindow *win = CTX_wm_window(C); - const wmEvent *event = win->eventstate; - - SpaceImage *sima = CTX_wm_space_image(C); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - const float color[3] = {1, 1, 1}; - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformColor3fv(color); - - /* TODO(campbell): lock to pixels. */ - rctf sample_rect_fl; - BLI_rctf_init_pt_radius( - &sample_rect_fl, - (float[2]){event->x - region->winrct.xmin, event->y - region->winrct.ymin}, - (float)(info->sample_size / 2.0f) * sima->zoom); - - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(GL_XOR); - GPU_line_width(1.0f); - imm_draw_box_wire_2d(pos, - (float)sample_rect_fl.xmin, - (float)sample_rect_fl.ymin, - (float)sample_rect_fl.xmax, - (float)sample_rect_fl.ymax); - glDisable(GL_COLOR_LOGIC_OP); - - immUnbindProgram(); - } -} - /* Returns color in linear space, matching ED_space_node_color_sample(). */ bool ED_space_image_color_sample(SpaceImage *sima, ARegion *region, int mval[2], float r_col[3]) { @@ -3222,279 +3122,6 @@ bool ED_space_image_color_sample(SpaceImage *sima, ARegion *region, int mval[2], return ret; } -/* -------------------------------------------------------------------- */ -/** \name Image Pixel Sample - * \{ */ - -static void image_sample_pixel_color_ubyte(const ImBuf *ibuf, - const int coord[2], - uchar r_col[4], - float r_col_linear[4]) -{ - const uchar *cp = (uchar *)(ibuf->rect + coord[1] * ibuf->x + coord[0]); - copy_v4_v4_uchar(r_col, cp); - rgba_uchar_to_float(r_col_linear, r_col); - IMB_colormanagement_colorspace_to_scene_linear_v4(r_col_linear, false, ibuf->rect_colorspace); -} - -static void image_sample_pixel_color_float(ImBuf *ibuf, const int coord[2], float r_col[4]) -{ - const float *cp = ibuf->rect_float + (ibuf->channels) * (coord[1] * ibuf->x + coord[0]); - copy_v4_v4(r_col, cp); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Image Pixel Region Sample - * \{ */ - -static void image_sample_rect_color_ubyte(const ImBuf *ibuf, - const rcti *rect, - uchar r_col[4], - float r_col_linear[4]) -{ - uint col_accum_ub[4] = {0, 0, 0, 0}; - zero_v4(r_col_linear); - int col_tot = 0; - int coord[2]; - for (coord[0] = rect->xmin; coord[0] <= rect->xmax; coord[0]++) { - for (coord[1] = rect->ymin; coord[1] <= rect->ymax; coord[1]++) { - float col_temp_fl[4]; - uchar col_temp_ub[4]; - image_sample_pixel_color_ubyte(ibuf, coord, col_temp_ub, col_temp_fl); - add_v4_v4(r_col_linear, col_temp_fl); - col_accum_ub[0] += (uint)col_temp_ub[0]; - col_accum_ub[1] += (uint)col_temp_ub[1]; - col_accum_ub[2] += (uint)col_temp_ub[2]; - col_accum_ub[3] += (uint)col_temp_ub[3]; - col_tot += 1; - } - } - mul_v4_fl(r_col_linear, 1.0 / (float)col_tot); - - r_col[0] = MIN2(col_accum_ub[0] / col_tot, 255); - r_col[1] = MIN2(col_accum_ub[1] / col_tot, 255); - r_col[2] = MIN2(col_accum_ub[2] / col_tot, 255); - r_col[3] = MIN2(col_accum_ub[3] / col_tot, 255); -} - -static void image_sample_rect_color_float(ImBuf *ibuf, const rcti *rect, float r_col[4]) -{ - zero_v4(r_col); - int col_tot = 0; - int coord[2]; - for (coord[0] = rect->xmin; coord[0] <= rect->xmax; coord[0]++) { - for (coord[1] = rect->ymin; coord[1] <= rect->ymax; coord[1]++) { - float col_temp_fl[4]; - image_sample_pixel_color_float(ibuf, coord, col_temp_fl); - add_v4_v4(r_col, col_temp_fl); - col_tot += 1; - } - } - mul_v4_fl(r_col, 1.0 / (float)col_tot); -} - -/** \} */ - -static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event) -{ - SpaceImage *sima = CTX_wm_space_image(C); - ARegion *region = CTX_wm_region(C); - Image *image = ED_space_image(sima); - - float uv[2]; - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]); - int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL); - - void *lock; - ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile); - ImageSampleInfo *info = op->customdata; - Scene *scene = CTX_data_scene(C); - CurveMapping *curve_mapping = scene->view_settings.curve_mapping; - - if (ibuf == NULL) { - ED_space_image_release_buffer(sima, ibuf, lock); - info->draw = false; - return; - } - - if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) { - int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y); - - CLAMP(x, 0, ibuf->x - 1); - CLAMP(y, 0, ibuf->y - 1); - - info->width = ibuf->x; - info->height = ibuf->y; - info->x = x; - info->y = y; - - info->draw = true; - info->channels = ibuf->channels; - - info->colp = NULL; - info->colfp = NULL; - info->zp = NULL; - info->zfp = NULL; - - info->use_default_view = (image->flag & IMA_VIEW_AS_RENDER) ? false : true; - - rcti sample_rect; - sample_rect.xmin = max_ii(0, x - info->sample_size / 2); - sample_rect.ymin = max_ii(0, y - info->sample_size / 2); - sample_rect.xmax = min_ii(ibuf->x, sample_rect.xmin + info->sample_size) - 1; - sample_rect.ymax = min_ii(ibuf->y, sample_rect.ymin + info->sample_size) - 1; - - if (ibuf->rect) { - image_sample_rect_color_ubyte(ibuf, &sample_rect, info->col, info->linearcol); - rgba_uchar_to_float(info->colf, info->col); - - info->colp = info->col; - info->colfp = info->colf; - info->color_manage = true; - } - if (ibuf->rect_float) { - image_sample_rect_color_float(ibuf, &sample_rect, info->colf); - - if (ibuf->channels == 4) { - /* pass */ - } - else if (ibuf->channels == 3) { - info->colf[3] = 1.0f; - } - else { - info->colf[1] = info->colf[0]; - info->colf[2] = info->colf[0]; - info->colf[3] = 1.0f; - } - info->colfp = info->colf; - - copy_v4_v4(info->linearcol, info->colf); - - info->color_manage = true; - } - - if (ibuf->zbuf) { - /* TODO, blend depth (not urgent). */ - info->z = ibuf->zbuf[y * ibuf->x + x]; - info->zp = &info->z; - if (ibuf->zbuf == (int *)ibuf->rect) { - info->colp = NULL; - } - } - if (ibuf->zbuf_float) { - /* TODO, blend depth (not urgent). */ - info->zf = ibuf->zbuf_float[y * ibuf->x + x]; - info->zfp = &info->zf; - if (ibuf->zbuf_float == ibuf->rect_float) { - info->colfp = NULL; - } - } - - if (curve_mapping && ibuf->channels == 4) { - /* we reuse this callback for set curves point operators */ - if (RNA_struct_find_property(op->ptr, "point")) { - int point = RNA_enum_get(op->ptr, "point"); - - if (point == 1) { - BKE_curvemapping_set_black_white(curve_mapping, NULL, info->linearcol); - } - else if (point == 0) { - BKE_curvemapping_set_black_white(curve_mapping, info->linearcol, NULL); - } - WM_event_add_notifier(C, NC_WINDOW, NULL); - } - } - - // XXX node curve integration .. -#if 0 - { - ScrArea *area, *cur = curarea; - - node_curvemap_sample(fp); /* sends global to node editor */ - for (area = G.curscreen->areabase.first; area; area = area->next) { - if (area->spacetype == SPACE_NODE) { - areawinset(area->win); - scrarea_do_windraw(area); - } - } - node_curvemap_sample(NULL); /* clears global in node editor */ - curarea = cur; - } -#endif - } - else { - info->draw = 0; - } - - ED_space_image_release_buffer(sima, ibuf, lock); - ED_area_tag_redraw(CTX_wm_area(C)); -} - -static void image_sample_exit(bContext *C, wmOperator *op) -{ - ImageSampleInfo *info = op->customdata; - - ED_region_draw_cb_exit(info->art, info->draw_handle); - ED_area_tag_redraw(CTX_wm_area(C)); - MEM_freeN(info); -} - -static int image_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - SpaceImage *sima = CTX_wm_space_image(C); - ARegion *region = CTX_wm_region(C); - ImageSampleInfo *info; - - if (region->regiontype == RGN_TYPE_WINDOW) { - if (event->mval[1] <= 16 && ED_space_image_show_cache(sima)) { - return OPERATOR_PASS_THROUGH; - } - } - - if (!ED_space_image_has_buffer(sima)) { - return OPERATOR_CANCELLED; - } - - info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); - - info->art = region->type; - info->draw_handle = ED_region_draw_cb_activate( - region->type, image_sample_draw, info, REGION_DRAW_POST_PIXEL); - info->sample_size = RNA_int_get(op->ptr, "size"); - op->customdata = info; - - image_sample_apply(C, op, event); - - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static int image_sample_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - switch (event->type) { - case LEFTMOUSE: - case RIGHTMOUSE: // XXX hardcoded - if (event->val == KM_RELEASE) { - image_sample_exit(C, op); - return OPERATOR_CANCELLED; - } - break; - case MOUSEMOVE: - image_sample_apply(C, op, event); - break; - } - - return OPERATOR_RUNNING_MODAL; -} - -static void image_sample_cancel(bContext *C, wmOperator *op) -{ - image_sample_exit(C, op); -} - void IMAGE_OT_sample(wmOperatorType *ot) { /* identifiers */ @@ -3503,10 +3130,10 @@ void IMAGE_OT_sample(wmOperatorType *ot) ot->description = "Use mouse to sample a color in current image"; /* api callbacks */ - ot->invoke = image_sample_invoke; - ot->modal = image_sample_modal; - ot->cancel = image_sample_cancel; - ot->poll = image_sample_poll; + ot->invoke = ED_imbuf_sample_invoke; + ot->modal = ED_imbuf_sample_modal; + ot->cancel = ED_imbuf_sample_cancel; + ot->poll = ED_imbuf_sample_poll; /* flags */ ot->flag = OPTYPE_BLOCKING; @@ -3632,9 +3259,9 @@ void IMAGE_OT_curves_point_set(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* api callbacks */ - ot->invoke = image_sample_invoke; - ot->modal = image_sample_modal; - ot->cancel = image_sample_cancel; + ot->invoke = ED_imbuf_sample_invoke; + ot->modal = ED_imbuf_sample_modal; + ot->cancel = ED_imbuf_sample_cancel; ot->poll = space_image_main_area_not_uv_brush_poll; /* properties */ diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index a890b770c83..d397c255b03 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -21,219 +21,16 @@ * \ingroup spseq */ -#include "MEM_guardedalloc.h" +#include "ED_util_imbuf.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" +#include "RNA_define.h" -#include "DNA_scene_types.h" - -#include "BKE_context.h" -#include "BKE_main.h" -#include "BKE_screen.h" -#include "BKE_sequencer.h" - -#include "WM_api.h" #include "WM_types.h" -#include "ED_image.h" -#include "ED_screen.h" -#include "ED_space_api.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "UI_view2d.h" - /* Own include. */ #include "sequencer_intern.h" /******************** sample backdrop operator ********************/ - -typedef struct ImageSampleInfo { - ARegionType *art; - void *draw_handle; - int x, y; - int channels; - - uchar col[4]; - float colf[4]; - float linearcol[4]; - - uchar *colp; - const float *colfp; - - int draw; - int color_manage; -} ImageSampleInfo; - -static void sample_draw(const bContext *C, ARegion *region, void *arg_info) -{ - Scene *scene = CTX_data_scene(C); - ImageSampleInfo *info = arg_info; - - if (info->draw) { - ED_image_draw_info(scene, - region, - info->color_manage, - false, - info->channels, - info->x, - info->y, - info->colp, - info->colfp, - info->linearcol, - NULL, - NULL); - } -} - -static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - Scene *scene = CTX_data_scene(C); - SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C); - ARegion *region = CTX_wm_region(C); - ImBuf *ibuf = sequencer_ibuf_get(bmain, depsgraph, scene, sseq, CFRA, 0, NULL); - ImageSampleInfo *info = op->customdata; - float fx, fy; - - if (ibuf == NULL) { - IMB_freeImBuf(ibuf); - info->draw = 0; - return; - } - - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &fx, &fy); - - fx /= scene->r.xasp / scene->r.yasp; - - fx += (float)scene->r.xsch / 2.0f; - fy += (float)scene->r.ysch / 2.0f; - fx *= (float)ibuf->x / (float)scene->r.xsch; - fy *= (float)ibuf->y / (float)scene->r.ysch; - - if (fx >= 0.0f && fy >= 0.0f && fx < ibuf->x && fy < ibuf->y) { - const float *fp; - uchar *cp; - int x = (int)fx, y = (int)fy; - - info->x = x; - info->y = y; - info->draw = 1; - info->channels = ibuf->channels; - - info->colp = NULL; - info->colfp = NULL; - - if (ibuf->rect) { - cp = (uchar *)(ibuf->rect + y * ibuf->x + x); - - info->col[0] = cp[0]; - info->col[1] = cp[1]; - info->col[2] = cp[2]; - info->col[3] = cp[3]; - info->colp = info->col; - - info->colf[0] = (float)cp[0] / 255.0f; - info->colf[1] = (float)cp[1] / 255.0f; - info->colf[2] = (float)cp[2] / 255.0f; - info->colf[3] = (float)cp[3] / 255.0f; - info->colfp = info->colf; - - copy_v4_v4(info->linearcol, info->colf); - IMB_colormanagement_colorspace_to_scene_linear_v4( - info->linearcol, false, ibuf->rect_colorspace); - - info->color_manage = true; - } - if (ibuf->rect_float) { - fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); - - info->colf[0] = fp[0]; - info->colf[1] = fp[1]; - info->colf[2] = fp[2]; - info->colf[3] = fp[3]; - info->colfp = info->colf; - - /* Sequencer's image buffers are in non-linear space, need to make them linear. */ - copy_v4_v4(info->linearcol, info->colf); - BKE_sequencer_pixel_from_sequencer_space_v4(scene, info->linearcol); - - info->color_manage = true; - } - } - else { - info->draw = 0; - } - - IMB_freeImBuf(ibuf); - ED_area_tag_redraw(CTX_wm_area(C)); -} - -static void sample_exit(bContext *C, wmOperator *op) -{ - ImageSampleInfo *info = op->customdata; - - ED_region_draw_cb_exit(info->art, info->draw_handle); - ED_area_tag_redraw(CTX_wm_area(C)); - MEM_freeN(info); -} - -static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - ARegion *region = CTX_wm_region(C); - SpaceSeq *sseq = CTX_wm_space_seq(C); - ImageSampleInfo *info; - - if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { - return OPERATOR_CANCELLED; - } - - info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); - info->art = region->type; - info->draw_handle = ED_region_draw_cb_activate( - region->type, sample_draw, info, REGION_DRAW_POST_PIXEL); - op->customdata = info; - - sample_apply(C, op, event); - - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -static int sample_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - switch (event->type) { - case LEFTMOUSE: - case RIGHTMOUSE: /* XXX hardcoded */ - if (event->val == KM_RELEASE) { - sample_exit(C, op); - return OPERATOR_CANCELLED; - } - break; - case MOUSEMOVE: - sample_apply(C, op, event); - break; - } - - return OPERATOR_RUNNING_MODAL; -} - -static void sample_cancel(bContext *C, wmOperator *op) -{ - sample_exit(C, op); -} - -static bool sample_poll(bContext *C) -{ - SpaceSeq *sseq = CTX_wm_space_seq(C); - return sseq && BKE_sequencer_editing_get(CTX_data_scene(C), false) != NULL; -} - void SEQUENCER_OT_sample(wmOperatorType *ot) { /* Identifiers. */ @@ -242,11 +39,17 @@ void SEQUENCER_OT_sample(wmOperatorType *ot) ot->description = "Use mouse to sample color in current frame"; /* Api callbacks. */ - ot->invoke = sample_invoke; - ot->modal = sample_modal; - ot->cancel = sample_cancel; - ot->poll = sample_poll; + ot->invoke = ED_imbuf_sample_invoke; + ot->modal = ED_imbuf_sample_modal; + ot->cancel = ED_imbuf_sample_cancel; + ot->poll = ED_imbuf_sample_poll; /* Flags. */ ot->flag = OPTYPE_BLOCKING; + + /* Not implemented. */ + PropertyRNA *prop; + prop = RNA_def_int(ot->srna, "size", 1, 1, 128, "Sample Size", "", 1, 64); + RNA_def_property_subtype(prop, PROP_PIXEL); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 987327eefc1..17a90d10ca7 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -17,6 +17,7 @@ set(INC ../include + ../space_sequencer ../../blenkernel ../../blenlib ../../blentranslation @@ -39,6 +40,7 @@ set(INC_SYS set(SRC ed_transverts.c ed_util.c + ed_util_imbuf.c gizmo_utils.c numinput.c select_utils.c @@ -91,6 +93,7 @@ set(SRC ../include/ED_undo.h ../include/ED_userpref.h ../include/ED_util.h + ../include/ED_util_imbuf.h ../include/ED_uvedit.h ../include/ED_view3d.h ../include/UI_icons.h diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c new file mode 100644 index 00000000000..0c4ddc2b809 --- /dev/null +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -0,0 +1,553 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edutil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_rect.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_screen.h" +#include "BKE_sequencer.h" + +#include "ED_image.h" +#include "ED_screen.h" +#include "ED_space_api.h" + +#include "GPU_immediate.h" +#include "GPU_state.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "UI_view2d.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "sequencer_intern.h" + +/* Own define. */ +#include "ED_util_imbuf.h" + +/* ********* Pixel sample operator ********* */ + +typedef struct ImageSampleInfo { + ARegionType *art; + void *draw_handle; + int x, y; + int channels; + + int width, height; + int sample_size; + + unsigned char col[4]; + float colf[4]; + float linearcol[4]; + int z; + float zf; + + unsigned char *colp; + const float *colfp; + int *zp; + float *zfp; + + bool draw; + bool color_manage; + int use_default_view; +} ImageSampleInfo; + +/* -------------------------------------------------------------------- */ +/** \name Image Pixel Sample + * \{ */ + +static void image_sample_pixel_color_ubyte(const ImBuf *ibuf, + const int coord[2], + uchar r_col[4], + float r_col_linear[4]) +{ + const uchar *cp = (unsigned char *)(ibuf->rect + coord[1] * ibuf->x + coord[0]); + copy_v4_v4_uchar(r_col, cp); + rgba_uchar_to_float(r_col_linear, r_col); + IMB_colormanagement_colorspace_to_scene_linear_v4(r_col_linear, false, ibuf->rect_colorspace); +} + +static void image_sample_pixel_color_float(ImBuf *ibuf, const int coord[2], float r_col[4]) +{ + const float *cp = ibuf->rect_float + (ibuf->channels) * (coord[1] * ibuf->x + coord[0]); + copy_v4_v4(r_col, cp); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Pixel Region Sample + * \{ */ + +static void image_sample_rect_color_ubyte(const ImBuf *ibuf, + const rcti *rect, + uchar r_col[4], + float r_col_linear[4]) +{ + uint col_accum_ub[4] = {0, 0, 0, 0}; + zero_v4(r_col_linear); + int col_tot = 0; + int coord[2]; + for (coord[0] = rect->xmin; coord[0] <= rect->xmax; coord[0]++) { + for (coord[1] = rect->ymin; coord[1] <= rect->ymax; coord[1]++) { + float col_temp_fl[4]; + uchar col_temp_ub[4]; + image_sample_pixel_color_ubyte(ibuf, coord, col_temp_ub, col_temp_fl); + add_v4_v4(r_col_linear, col_temp_fl); + col_accum_ub[0] += (uint)col_temp_ub[0]; + col_accum_ub[1] += (uint)col_temp_ub[1]; + col_accum_ub[2] += (uint)col_temp_ub[2]; + col_accum_ub[3] += (uint)col_temp_ub[3]; + col_tot += 1; + } + } + mul_v4_fl(r_col_linear, 1.0 / (float)col_tot); + + r_col[0] = MIN2(col_accum_ub[0] / col_tot, 255); + r_col[1] = MIN2(col_accum_ub[1] / col_tot, 255); + r_col[2] = MIN2(col_accum_ub[2] / col_tot, 255); + r_col[3] = MIN2(col_accum_ub[3] / col_tot, 255); +} + +static void image_sample_rect_color_float(ImBuf *ibuf, const rcti *rect, float r_col[4]) +{ + zero_v4(r_col); + int col_tot = 0; + int coord[2]; + for (coord[0] = rect->xmin; coord[0] <= rect->xmax; coord[0]++) { + for (coord[1] = rect->ymin; coord[1] <= rect->ymax; coord[1]++) { + float col_temp_fl[4]; + image_sample_pixel_color_float(ibuf, coord, col_temp_fl); + add_v4_v4(r_col, col_temp_fl); + col_tot += 1; + } + } + mul_v4_fl(r_col, 1.0 / (float)col_tot); +} + +/** \} */ + +static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event) +{ + SpaceImage *sima = CTX_wm_space_image(C); + ARegion *region = CTX_wm_region(C); + Image *image = ED_space_image(sima); + + float uv[2]; + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]); + int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL); + + void *lock; + ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile); + ImageSampleInfo *info = op->customdata; + Scene *scene = CTX_data_scene(C); + CurveMapping *curve_mapping = scene->view_settings.curve_mapping; + + if (ibuf == NULL) { + ED_space_image_release_buffer(sima, ibuf, lock); + info->draw = false; + return; + } + + if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) { + int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y); + + CLAMP(x, 0, ibuf->x - 1); + CLAMP(y, 0, ibuf->y - 1); + + info->width = ibuf->x; + info->height = ibuf->y; + info->x = x; + info->y = y; + + info->draw = true; + info->channels = ibuf->channels; + + info->colp = NULL; + info->colfp = NULL; + info->zp = NULL; + info->zfp = NULL; + + info->use_default_view = (image->flag & IMA_VIEW_AS_RENDER) ? false : true; + + rcti sample_rect; + sample_rect.xmin = max_ii(0, x - info->sample_size / 2); + sample_rect.ymin = max_ii(0, y - info->sample_size / 2); + sample_rect.xmax = min_ii(ibuf->x, sample_rect.xmin + info->sample_size) - 1; + sample_rect.ymax = min_ii(ibuf->y, sample_rect.ymin + info->sample_size) - 1; + + if (ibuf->rect) { + image_sample_rect_color_ubyte(ibuf, &sample_rect, info->col, info->linearcol); + rgba_uchar_to_float(info->colf, info->col); + + info->colp = info->col; + info->colfp = info->colf; + info->color_manage = true; + } + if (ibuf->rect_float) { + image_sample_rect_color_float(ibuf, &sample_rect, info->colf); + + if (ibuf->channels == 4) { + /* pass */ + } + else if (ibuf->channels == 3) { + info->colf[3] = 1.0f; + } + else { + info->colf[1] = info->colf[0]; + info->colf[2] = info->colf[0]; + info->colf[3] = 1.0f; + } + info->colfp = info->colf; + + copy_v4_v4(info->linearcol, info->colf); + + info->color_manage = true; + } + + if (ibuf->zbuf) { + /* TODO, blend depth (not urgent). */ + info->z = ibuf->zbuf[y * ibuf->x + x]; + info->zp = &info->z; + if (ibuf->zbuf == (int *)ibuf->rect) { + info->colp = NULL; + } + } + if (ibuf->zbuf_float) { + /* TODO, blend depth (not urgent). */ + info->zf = ibuf->zbuf_float[y * ibuf->x + x]; + info->zfp = &info->zf; + if (ibuf->zbuf_float == ibuf->rect_float) { + info->colfp = NULL; + } + } + + if (curve_mapping && ibuf->channels == 4) { + /* we reuse this callback for set curves point operators */ + if (RNA_struct_find_property(op->ptr, "point")) { + int point = RNA_enum_get(op->ptr, "point"); + + if (point == 1) { + BKE_curvemapping_set_black_white(curve_mapping, NULL, info->linearcol); + } + else if (point == 0) { + BKE_curvemapping_set_black_white(curve_mapping, info->linearcol, NULL); + } + WM_event_add_notifier(C, NC_WINDOW, NULL); + } + } + + // XXX node curve integration .. +#if 0 + { + ScrArea *sa, *cur = curarea; + + node_curvemap_sample(fp); /* sends global to node editor */ + for (sa = G.curscreen->areabase.first; sa; sa = sa->next) { + if (sa->spacetype == SPACE_NODE) { + areawinset(sa->win); + scrarea_do_windraw(sa); + } + } + node_curvemap_sample(NULL); /* clears global in node editor */ + curarea = cur; + } +#endif + } + else { + info->draw = 0; + } + + ED_space_image_release_buffer(sima, ibuf, lock); + ED_area_tag_redraw(CTX_wm_area(C)); +} + +static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + SpaceSeq *sseq = (SpaceSeq *)CTX_wm_space_data(C); + ARegion *region = CTX_wm_region(C); + ImBuf *ibuf = sequencer_ibuf_get(bmain, depsgraph, scene, sseq, CFRA, 0, NULL); + ImageSampleInfo *info = op->customdata; + float fx, fy; + + if (ibuf == NULL) { + IMB_freeImBuf(ibuf); + info->draw = 0; + return; + } + + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &fx, &fy); + + fx /= scene->r.xasp / scene->r.yasp; + + fx += (float)scene->r.xsch / 2.0f; + fy += (float)scene->r.ysch / 2.0f; + fx *= (float)ibuf->x / (float)scene->r.xsch; + fy *= (float)ibuf->y / (float)scene->r.ysch; + + if (fx >= 0.0f && fy >= 0.0f && fx < ibuf->x && fy < ibuf->y) { + const float *fp; + unsigned char *cp; + int x = (int)fx, y = (int)fy; + + info->x = x; + info->y = y; + info->draw = 1; + info->channels = ibuf->channels; + + info->colp = NULL; + info->colfp = NULL; + + if (ibuf->rect) { + cp = (unsigned char *)(ibuf->rect + y * ibuf->x + x); + + info->col[0] = cp[0]; + info->col[1] = cp[1]; + info->col[2] = cp[2]; + info->col[3] = cp[3]; + info->colp = info->col; + + info->colf[0] = (float)cp[0] / 255.0f; + info->colf[1] = (float)cp[1] / 255.0f; + info->colf[2] = (float)cp[2] / 255.0f; + info->colf[3] = (float)cp[3] / 255.0f; + info->colfp = info->colf; + + copy_v4_v4(info->linearcol, info->colf); + IMB_colormanagement_colorspace_to_scene_linear_v4( + info->linearcol, false, ibuf->rect_colorspace); + + info->color_manage = true; + } + if (ibuf->rect_float) { + fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x)); + + info->colf[0] = fp[0]; + info->colf[1] = fp[1]; + info->colf[2] = fp[2]; + info->colf[3] = fp[3]; + info->colfp = info->colf; + + /* sequencer's image buffers are in non-linear space, need to make them linear */ + copy_v4_v4(info->linearcol, info->colf); + BKE_sequencer_pixel_from_sequencer_space_v4(scene, info->linearcol); + + info->color_manage = true; + } + } + else { + info->draw = 0; + } + + IMB_freeImBuf(ibuf); + ED_area_tag_redraw(CTX_wm_area(C)); +} + +static void ed_imbuf_sample_apply(bContext *C, wmOperator *op, const wmEvent *event) +{ + ScrArea *sa = CTX_wm_area(C); + + if (sa && sa->spacetype == SPACE_IMAGE) { + image_sample_apply(C, op, event); + } + + if (sa && sa->spacetype == SPACE_SEQ) { + sequencer_sample_apply(C, op, event); + } +} + +void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info) +{ + ImageSampleInfo *info = arg_info; + if (!info->draw) { + return; + } + + Scene *scene = CTX_data_scene(C); + ED_image_draw_info(scene, + region, + info->color_manage, + info->use_default_view, + info->channels, + info->x, + info->y, + info->colp, + info->colfp, + info->linearcol, + info->zp, + info->zfp); + + if (info->sample_size > 1) { + ScrArea *sa = CTX_wm_area(C); + + if (sa && sa->spacetype == SPACE_IMAGE) { + + const wmWindow *win = CTX_wm_window(C); + const wmEvent *event = win->eventstate; + + SpaceImage *sima = CTX_wm_space_image(C); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + const float color[3] = {1, 1, 1}; + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fv(color); + + /* TODO(campbell): lock to pixels. */ + rctf sample_rect_fl; + BLI_rctf_init_pt_radius( + &sample_rect_fl, + (float[2]){event->x - region->winrct.xmin, event->y - region->winrct.ymin}, + (float)(info->sample_size / 2.0f) * sima->zoom); + + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + GPU_line_width(1.0f); + imm_draw_box_wire_2d(pos, + (float)sample_rect_fl.xmin, + (float)sample_rect_fl.ymin, + (float)sample_rect_fl.xmax, + (float)sample_rect_fl.ymax); + glDisable(GL_COLOR_LOGIC_OP); + + immUnbindProgram(); + } + } +} + +void ED_imbuf_sample_exit(bContext *C, wmOperator *op) +{ + ImageSampleInfo *info = op->customdata; + + ED_region_draw_cb_exit(info->art, info->draw_handle); + ED_area_tag_redraw(CTX_wm_area(C)); + MEM_freeN(info); +} + +int ED_imbuf_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + ImageSampleInfo *info; + + info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); + + info->art = region->type; + info->draw_handle = ED_region_draw_cb_activate( + region->type, ED_imbuf_sample_draw, info, REGION_DRAW_POST_PIXEL); + info->sample_size = RNA_int_get(op->ptr, "size"); + op->customdata = info; + + ScrArea *sa = CTX_wm_area(C); + + if (sa && sa->spacetype == SPACE_IMAGE) { + SpaceImage *sima = CTX_wm_space_image(C); + + if (region->regiontype == RGN_TYPE_WINDOW) { + if (event->mval[1] <= 16 && ED_space_image_show_cache(sima)) { + return OPERATOR_PASS_THROUGH; + } + } + + if (!ED_space_image_has_buffer(sima)) { + return OPERATOR_CANCELLED; + } + } + + ed_imbuf_sample_apply(C, op, event); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +int ED_imbuf_sample_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + switch (event->type) { + case LEFTMOUSE: + case RIGHTMOUSE: // XXX hardcoded + if (event->val == KM_RELEASE) { + ED_imbuf_sample_exit(C, op); + return OPERATOR_CANCELLED; + } + break; + case MOUSEMOVE: + ed_imbuf_sample_apply(C, op, event); + break; + } + + return OPERATOR_RUNNING_MODAL; +} + +void ED_imbuf_sample_cancel(bContext *C, wmOperator *op) +{ + ED_imbuf_sample_exit(C, op); +} + +bool ED_imbuf_sample_poll(bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + + if (sa && sa->spacetype == SPACE_IMAGE) { + SpaceImage *sima = CTX_wm_space_image(C); + if (sima == NULL) { + return false; + } + + Object *obedit = CTX_data_edit_object(C); + if (obedit) { + /* Disable when UV editing so it doesn't swallow all click events + * (use for setting cursor). */ + if (ED_space_image_show_uvedit(sima, obedit)) { + return false; + } + } + else if (sima->mode != SI_MODE_VIEW) { + return false; + } + + return true; + } + + if (sa && sa->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = CTX_wm_space_seq(C); + + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return false; + } + + return sseq && BKE_sequencer_editing_get(CTX_data_scene(C), false) != NULL; + } + + return false; +} -- cgit v1.2.3 From 68ba6378b5b14059e52a26306bc8b9f3db2e7299 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 13 Apr 2020 00:28:27 +0200 Subject: VSE: Add sample tool This tool is set as default tool, so default action on click doesn't have pernament effect. Reviewed By: campbellbarton Differential Revision: D7064 --- .../presets/keyconfig/keymap_data/blender_default.py | 11 +++++++++++ .../scripts/startup/bl_ui/space_toolsystem_toolbar.py | 17 +++++++++++++++++ source/blender/windowmanager/intern/wm_toolsystem.c | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 3944f8f0817..90df48581bf 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6514,6 +6514,16 @@ def km_sequencer_editor_tool_select_box(params): ) +def km_sequencer_editor_tool_generic_sample(params): + return ( + "Sequencer Tool: Sample", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("sequencer.sample", {"type": params.tool_mouse, "value": 'PRESS'}, None), + ]}, + ) + + def km_sequencer_editor_tool_blade(_params): return ( "Sequencer Tool: Blade", @@ -6758,6 +6768,7 @@ def generate_keymaps(params=None): km_sequencer_editor_tool_select(params), km_sequencer_editor_tool_select_box(params), km_sequencer_editor_tool_blade(params), + km_sequencer_editor_tool_generic_sample(params), ] # ------------------------------------------------------------------------------ diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index cb3d2909205..5b7db2342e5 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1807,6 +1807,21 @@ class _defs_sequencer_generic: draw_settings=draw_settings, ) + @ToolDef.from_fn + def sample(): + def draw_settings(_context, layout, tool): + props = tool.operator_properties("sequencer.sample") + return dict( + idname="builtin.sample", + label="Sample", + description=( + "Sample pixel values under the cursor" + ), + icon="ops.paint.weight_sample", # XXX, needs own icon. + keymap="Sequencer Tool: Sample", + draw_settings=draw_settings, + ) + class _defs_sequencer_select: @ToolDef.from_fn @@ -2348,6 +2363,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): None: [ ], 'PREVIEW': [ + _defs_sequencer_generic.sample, *_tools_annotate, ], 'SEQUENCER': [ @@ -2355,6 +2371,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sequencer_generic.blade, ], 'SEQUENCER_PREVIEW': [ + _defs_sequencer_generic.sample, *_tools_select, *_tools_annotate, _defs_sequencer_generic.blade, diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 38fa24f0416..72969f34162 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -707,7 +707,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) case SEQ_VIEW_SEQUENCE: return "builtin.select"; case SEQ_VIEW_PREVIEW: - return "builtin.annotate"; + return "builtin.sample"; case SEQ_VIEW_SEQUENCE_PREVIEW: return "builtin.select"; } -- cgit v1.2.3 From 71a333f56e40b6cedf1dcb21c281b5df7123d468 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Sun, 12 Apr 2020 18:17:59 -0500 Subject: Fix T75592: Correctly calculate length of curve verts Previous commit to fix T75405 needed a small change to increase the length of the front section of the curve when only the front is built. --- source/blender/blenkernel/intern/curve.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 401014e0853..b4a8625c0bb 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -1877,7 +1877,9 @@ void BKE_curve_bevel_make(Object *ob, ListBase *disp) } /* Don't duplicate the last back vertex. */ angle = (cu->ext1 == 0.0f && (cu->flag & CU_BACK)) ? dangle : 0; - int front_len = (cu->ext1 == 0.0f) ? cu->bevresol + 1 : cu->bevresol + 2; + int front_len = (cu->ext1 == 0.0f && ((cu->flag & CU_BACK) || !(cu->flag & CU_FRONT))) ? + cu->bevresol + 1 : + cu->bevresol + 2; for (a = 0; a < front_len; a++) { fp[0] = 0.0; fp[1] = (float)(cosf(angle) * (cu->ext2)); -- cgit v1.2.3 From e63d5f40eef3349f26205d42cf7d11f9c01df6d9 Mon Sep 17 00:00:00 2001 From: William Reynish Date: Mon, 13 Apr 2020 07:49:21 +0200 Subject: Fix T75667: Use of incorrect terminology in the brush_colors_flip operator - The word 'Flip' is incorrect. 'Swap' or 'Switch' is correct. - In Blender, we use 'primary' & 'secondary' color swatches, not 'foreground' and 'background' --- source/blender/editors/sculpt_paint/paint_image.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 4d4af028570..1a2b44a03b4 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -1288,9 +1288,9 @@ static bool brush_colors_flip_poll(bContext *C) void PAINT_OT_brush_colors_flip(wmOperatorType *ot) { /* identifiers */ - ot->name = "Brush Colors Flip"; + ot->name = "Swap Colors"; ot->idname = "PAINT_OT_brush_colors_flip"; - ot->description = "Toggle foreground and background brush colors"; + ot->description = "Swap primary and secondary brush colors"; /* api callbacks */ ot->exec = brush_colors_flip_exec; -- cgit v1.2.3 From 2ec6eca518ef85115cbb7b6465f296d2fd422f5e Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 13 Apr 2020 10:07:12 +0200 Subject: Cleanup: unused parameter... --- source/blender/makesrna/intern/rna_sequencer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index b135c962891..d3eae4562ec 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -287,7 +287,9 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) /* A simple wrapper around above func, directly usable as prop update func. * Also invalidate cache if needed. */ -static void rna_Sequence_frame_change_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) +static void rna_Sequence_frame_change_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) { Scene *scene = (Scene *)ptr->owner_id; do_sequence_frame_change_update(scene, (Sequence *)ptr->data); -- cgit v1.2.3 From 2e75172c45fd7f434aa80d4bb04d74e3449a4ea1 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 13 Apr 2020 15:15:47 +0200 Subject: Fix T75677: Annotation in compositor/shading tabs loose AA after drawing stroke The drawing of annotations in 2D was using a very old code created in 2.6x versions. Now it's using a standard shader as it's done while drawing. --- source/blender/editors/gpencil/annotate_draw.c | 238 ++++++++----------------- 1 file changed, 73 insertions(+), 165 deletions(-) diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index 26ba2661072..adaf4ab2459 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -328,13 +328,10 @@ static void annotation_draw_stroke_3d( immUnbindProgram(); } -/* ----- Fancy 2D-Stroke Drawing ------ */ - -/* draw a given stroke in 2d */ +/* Draw a given stroke in 2d. */ static void annotation_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thickness_s, - short dflag, short sflag, int offsx, int offsy, @@ -342,167 +339,84 @@ static void annotation_draw_stroke_2d(const bGPDspoint *points, int winy, const float ink[4]) { - /* otherwise thickness is twice that of the 3D view */ - float thickness = (float)thickness_s * 0.5f; - - /* strokes in Image Editor need a scale factor, since units there are not pixels! */ - float scalefac = 1.0f; - if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) { - scalefac = 0.001f; + if (totpoints == 0) { + return; } + float thickness = (float)thickness_s; - /* Tessellation code - draw stroke as series of connected quads - * (triangle strips in fact) with connection edges rotated to minimize shrinking artifacts, - * and rounded end-caps. - */ - { - const bGPDspoint *pt1, *pt2; - float s0[2], s1[2]; /* segment 'center' points */ - float pm[2]; /* normal from previous segment. */ - int i; + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const bGPDspoint *pt; + const bGPDspoint *pt_prev; + int draw_points = 0; + float co[2]; + float oldpressure = points[0].pressure; + if (totpoints == 1) { + /* if drawing a single point, draw it larger */ + GPU_point_size((float)(thickness + 2) * points->pressure); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + immUniformColor3fvAlpha(ink, ink[3]); + immBegin(GPU_PRIM_POINTS, 1); + + annotation_calc_2d_stroke_fxy(&points->x, sflag, offsx, offsy, winx, winy, co); + immVertex2fv(pos, co); + } + else { + /* draw stroke curve */ + GPU_line_width(max_ff(oldpressure * thickness, 1.0)); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3fvAlpha(ink, ink[3]); - immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4); - - /* get x and y coordinates from first point */ - annotation_calc_2d_stroke_fxy(&points->x, sflag, offsx, offsy, winx, winy, s0); - - for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { - float t0[2], t1[2]; /* tessellated coordinates */ - float m1[2], m2[2]; /* gradient and normal */ - float mt[2], sc[2]; /* gradient for thickness, point for end-cap */ - float pthick; /* thickness at segment point */ - - /* Get x and y coordinates from point2 - * (point1 has already been computed in previous iteration). */ - annotation_calc_2d_stroke_fxy(&pt2->x, sflag, offsx, offsy, winx, winy, s1); - - /* calculate gradient and normal - 'angle'=(ny/nx) */ - m1[1] = s1[1] - s0[1]; - m1[0] = s1[0] - s0[0]; - normalize_v2(m1); - m2[1] = -m1[0]; - m2[0] = m1[1]; - - /* always use pressure from first point here */ - pthick = (pt1->pressure * thickness * scalefac); - - /* if the first segment, start of segment is segment's normal */ - if (i == 0) { - /* draw start cap first - * - make points slightly closer to center (about halfway across) - */ - mt[0] = m2[0] * pthick * 0.5f; - mt[1] = m2[1] * pthick * 0.5f; - sc[0] = s0[0] - (m1[0] * pthick * 0.75f); - sc[1] = s0[1] - (m1[1] * pthick * 0.75f); - - t0[0] = sc[0] - mt[0]; - t0[1] = sc[1] - mt[1]; - t1[0] = sc[0] + mt[0]; - t1[1] = sc[1] + mt[1]; - - /* First two points of cap. */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - - /* calculate points for start of segment */ - mt[0] = m2[0] * pthick; - mt[1] = m2[1] * pthick; - - t0[0] = s0[0] - mt[0]; - t0[1] = s0[1] - mt[1]; - t1[0] = s0[0] + mt[0]; - t1[1] = s0[1] + mt[1]; - - /* Last two points of start cap (and first two points of first segment). */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - } - /* if not the first segment, use bisector of angle between segments */ - else { - float mb[2]; /* bisector normal */ - float athick, dfac; /* actual thickness, difference between thicknesses */ - - /* calculate gradient of bisector (as average of normals) */ - mb[0] = (pm[0] + m2[0]) / 2; - mb[1] = (pm[1] + m2[1]) / 2; - normalize_v2(mb); - - /* calculate gradient to apply - * - as basis, use just pthick * bisector gradient - * - if cross-section not as thick as it should be, add extra padding to fix it - */ - mt[0] = mb[0] * pthick; - mt[1] = mb[1] * pthick; - athick = len_v2(mt); - dfac = pthick - (athick * 2); - - if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) { - mt[0] += (mb[0] * dfac); - mt[1] += (mb[1] * dfac); + + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints); + + for (int i = 0; i < totpoints; i++) { + pt = &points[i]; + /* If there was a significant pressure change, + * stop the curve, change the thickness of the stroke, + * and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP). + */ + if (fabsf(pt->pressure - oldpressure) > 0.2f) { + /* need to have 2 points to avoid immEnd assert error */ + if (draw_points < 2) { + pt_prev = &points[i - 1]; + annotation_calc_2d_stroke_fxy(&pt_prev->x, sflag, offsx, offsy, winx, winy, co); + immVertex2fv(pos, co); } - /* calculate points for start of segment */ - t0[0] = s0[0] - mt[0]; - t0[1] = s0[1] - mt[1]; - t1[0] = s0[0] + mt[0]; - t1[1] = s0[1] + mt[1]; + immEnd(); + draw_points = 0; - /* Last two points of previous segment, and first two points of current segment. */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - } + GPU_line_width(max_ff(pt->pressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1); + + /* need to roll-back one point to ensure that there are no gaps in the stroke */ + if (i != 0) { + pt_prev = &points[i - 1]; + annotation_calc_2d_stroke_fxy(&pt_prev->x, sflag, offsx, offsy, winx, winy, co); + immVertex2fv(pos, co); + draw_points++; + } - /* if last segment, also draw end of segment (defined as segment's normal) */ - if (i == totpoints - 2) { - /* for once, we use second point's pressure (otherwise it won't be drawn) */ - pthick = (pt2->pressure * thickness * scalefac); - - /* calculate points for end of segment */ - mt[0] = m2[0] * pthick; - mt[1] = m2[1] * pthick; - - t0[0] = s1[0] - mt[0]; - t0[1] = s1[1] - mt[1]; - t1[0] = s1[0] + mt[0]; - t1[1] = s1[1] + mt[1]; - - /* Last two points of last segment (and first two points of end cap). */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); - - /* draw end cap as last step - * - make points slightly closer to center (about halfway across) - */ - mt[0] = m2[0] * pthick * 0.5f; - mt[1] = m2[1] * pthick * 0.5f; - sc[0] = s1[0] + (m1[0] * pthick * 0.75f); - sc[1] = s1[1] + (m1[1] * pthick * 0.75f); - - t0[0] = sc[0] - mt[0]; - t0[1] = sc[1] - mt[1]; - t1[0] = sc[0] + mt[0]; - t1[1] = sc[1] + mt[1]; - - /* Last two points of end cap. */ - immVertex2fv(pos, t0); - immVertex2fv(pos, t1); + oldpressure = pt->pressure; /* reset our threshold */ } - /* store computed point2 coordinates as point1 ones of next segment. */ - copy_v2_v2(s0, s1); - /* store stroke's 'natural' normal for next stroke to use */ - copy_v2_v2(pm, m2); + /* now the point we want */ + annotation_calc_2d_stroke_fxy(&pt->x, sflag, offsx, offsy, winx, winy, co); + immVertex2fv(pos, co); + draw_points++; + } + /* need to have 2 points to avoid immEnd assert error */ + if (draw_points < 2) { + pt_prev = &points[0]; + annotation_calc_2d_stroke_fxy(&pt_prev->x, sflag, offsx, offsy, winx, winy, co); + immVertex2fv(pos, co); } - - immEnd(); - immUnbindProgram(); } + + immEnd(); + immUnbindProgram(); } /* ----- Strokes Drawing ------ */ @@ -601,16 +515,8 @@ static void annotation_draw_strokes(const bGPDframe *gpf, gps->points, lthick, gps->flag, offsx, offsy, winx, winy, color); } else { - annotation_draw_stroke_2d(gps->points, - gps->totpoints, - lthick, - dflag, - gps->flag, - offsx, - offsy, - winx, - winy, - color); + annotation_draw_stroke_2d( + gps->points, gps->totpoints, lthick, gps->flag, offsx, offsy, winx, winy, color); } } } @@ -862,7 +768,8 @@ static void annotation_draw_data_layers( annotation_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, lthick, ink); /* Draw verts of selected strokes: - * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering + * - when doing OpenGL renders, we don't want to be showing these, as that ends up + * flickering * - locked layers can't be edited, so there's no point showing these verts * as they will have no bearings on what gets edited * - only show when in editmode, since operators shouldn't work otherwise @@ -1126,8 +1033,8 @@ void ED_annotation_draw_view2d(const bContext *C, bool onlyv2d) } } -/* draw annotations sketches to specified 3d-view assuming that matrices are already set correctly - * Note: this gets called twice - first time with only3d=true to draw 3d-strokes, +/* draw annotations sketches to specified 3d-view assuming that matrices are already set + * correctly Note: this gets called twice - first time with only3d=true to draw 3d-strokes, * second time with only3d=false for screen-aligned strokes */ void ED_annotation_draw_view3d( Scene *scene, struct Depsgraph *depsgraph, View3D *v3d, ARegion *region, bool only3d) @@ -1137,7 +1044,8 @@ void ED_annotation_draw_view3d( int offsx, offsy, winx, winy; /* check that we have grease-pencil stuff to draw */ - /* XXX: Hardcoded reference here may get out of sync if we change how we fetch annotation data */ + /* XXX: Hardcoded reference here may get out of sync if we change how we fetch annotation data + */ bGPdata *gpd = scene->gpd; if (gpd == NULL) { return; -- cgit v1.2.3 From c19f37764db96f5f5c781e8299a74a25a28e8ec2 Mon Sep 17 00:00:00 2001 From: William Reynish Date: Mon, 13 Apr 2020 15:47:41 +0200 Subject: UI: Fix wrong icon used for Unified Color toggle --- release/scripts/startup/bl_ui/properties_paint_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 0b006c9cea3..32b386bf0ab 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -902,7 +902,7 @@ def draw_color_settings(context, layout, brush, color_type=False): UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") row.separator() row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) - row.prop(ups, "use_unified_color", text="", icon='WORLD') + row.prop(ups, "use_unified_color", text="", icon='BRUSHES_ALL') # Gradient elif brush.color_type == 'GRADIENT': layout.template_color_ramp(brush, "gradient", expand=True) -- cgit v1.2.3 From 5cf72833429f4c63d27ea0a72878af1fec6dcf4e Mon Sep 17 00:00:00 2001 From: Henrik Dick Date: Mon, 13 Apr 2020 17:15:16 +0200 Subject: Fix T75032: New complex solidify algorithm handles poorly merging threshold of small geometry details. * Implemented the algortihm that would merge vertices to the weighted center between them. * Exposed the merge threshold to the user. The new default tolerance is 0.0001 (versionning code ensures that previous default value remains in use to avoid any change in existing files). Review and minor changes/cleanups from Bastien Montagne (@mont29). --- .../startup/bl_ui/properties_data_modifier.py | 2 + source/blender/blenloader/intern/versioning_280.c | 14 +++- source/blender/makesdna/DNA_modifier_types.h | 3 + source/blender/makesrna/intern/rna_modifier.c | 7 ++ source/blender/modifiers/intern/MOD_solidify.c | 1 + .../modifiers/intern/MOD_solidify_nonmanifold.c | 77 +++++++++++++--------- 6 files changed, 73 insertions(+), 31 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index 61151b3e02b..62e19129923 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -1049,6 +1049,8 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): col.prop(md, "edge_crease_inner", text="Inner") col.prop(md, "edge_crease_outer", text="Outer") col.prop(md, "edge_crease_rim", text="Rim") + else: + col.prop(md, "nonmanifold_merge_threshold") col = split.column() diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 5c99760dc9c..d53b6cde4ae 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4890,7 +4890,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (!MAIN_VERSION_ATLEAST(bmain, 283, 12)) { - /* Activate f-curve drawing in the sequencer. */ for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { for (ScrArea *area = screen->areabase.first; area; area = area->next) { @@ -4929,5 +4928,18 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Solidify modifier merge tolerance. */ + if (!DNA_struct_elem_find(fd->filesdna, "SolidifyModifierData", "float", "merge_tolerance")) { + for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) { + for (ModifierData *md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Solidify) { + SolidifyModifierData *smd = (SolidifyModifierData *)md; + /* set to 0.0003 since that is what was used before, default now is 0.0001 */ + smd->merge_tolerance = 0.0003f; + } + } + } + } } } diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index b2b2a73848a..431fcb7a243 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1175,6 +1175,9 @@ typedef struct SolidifyModifierData { int flag; short mat_ofs; short mat_ofs_rim; + + float merge_tolerance; + char _pad1[4]; } SolidifyModifierData; /** #SolidifyModifierData.flag */ diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 176d8e6de91..2c4818eb972 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -4523,6 +4523,13 @@ static void rna_def_modifier_solidify(BlenderRNA *brna) RNA_def_property_enum_items(prop, nonmanifold_boundary_mode_items); RNA_def_property_ui_text(prop, "Boundary Shape", "Selects the boundary adjustment algorithm"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "nonmanifold_merge_threshold", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "merge_tolerance"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4); + RNA_def_property_ui_text(prop, "Merge Threshold", "Distance within which degenerated geometry is merged"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); } static void rna_def_modifier_screw(BlenderRNA *brna) diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 0eed2335ed7..74d9df7d093 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -53,6 +53,7 @@ static void initData(ModifierData *md) smd->mode = MOD_SOLIDIFY_MODE_EXTRUDE; smd->nonmanifold_offset_mode = MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS; smd->nonmanifold_boundary_mode = MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE; + smd->merge_tolerance = 0.0001f; } static void requiredDataMask(Object *UNUSED(ob), diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index 0f9f5952a26..1cf9def46fa 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -291,6 +291,15 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, /* Vert edge adjacent map. */ OldVertEdgeRef **vert_adj_edges = MEM_calloc_arrayN( numVerts, sizeof(*vert_adj_edges), "vert_adj_edges in solidify"); + /* Original vertex positions (changed for degenerated geometry). */ + float(*orig_mvert_co)[3] = MEM_malloc_arrayN( + numVerts, sizeof(*orig_mvert_co), "orig_mvert_co in solidify"); + /* Fill in the original vertex positions. */ + for (uint i = 0; i < numVerts; i++) { + orig_mvert_co[i][0] = orig_mvert[i].co[0]; + orig_mvert_co[i][1] = orig_mvert[i].co[1]; + orig_mvert_co[i][2] = orig_mvert[i].co[2]; + } /* Create edge to #NewEdgeRef map. */ { @@ -344,33 +353,35 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, bool *face_singularity = MEM_calloc_arrayN( numPolys, sizeof(*face_singularity), "face_sides_arr in solidify"); + const float merge_tolerance_sqr = smd->merge_tolerance * smd->merge_tolerance; + uint *combined_verts = MEM_calloc_arrayN( + numVerts, sizeof(*combined_verts), "combined_verts in solidify"); + ed = orig_medge; for (uint i = 0; i < numEdges; i++, ed++) { if (edge_adj_faces_len[i] > 0) { - const uint v1 = vm[ed->v1]; - const uint v2 = vm[ed->v2]; + uint v1 = vm[ed->v1]; + uint v2 = vm[ed->v2]; if (v1 != v2) { - sub_v3_v3v3(edgedir, orig_mvert[v2].co, orig_mvert[v1].co); + if (v2 < v1) { + SWAP(uint, v1, v2); + } + sub_v3_v3v3(edgedir, orig_mvert_co[v2], orig_mvert_co[v1]); orig_edge_lengths[i] = len_squared_v3(edgedir); - if (orig_edge_lengths[i] <= FLT_EPSILON) { - if (v2 > v1) { - for (uint j = v2; j < numVerts; j++) { - if (vm[j] == v2) { - vm[j] = v1; - vert_adj_edges_len[v1] += vert_adj_edges_len[j]; - vert_adj_edges_len[j] = 0; - } - } - } - else if (v2 < v1) { - for (uint j = v1; j < numVerts; j++) { - if (vm[j] == v1) { - vm[j] = v2; - vert_adj_edges_len[v2] += vert_adj_edges_len[j]; - vert_adj_edges_len[j] = 0; - } + if (orig_edge_lengths[i] <= merge_tolerance_sqr) { + mul_v3_fl(edgedir, + (combined_verts[v2] + 1) / + (float)(combined_verts[v1] + combined_verts[v2] + 2)); + add_v3_v3(orig_mvert_co[v1], edgedir); + for (uint j = v2; j < numVerts; j++) { + if (vm[j] == v2) { + vm[j] = v1; } } + vert_adj_edges_len[v1] += vert_adj_edges_len[v2]; + vert_adj_edges_len[v2] = 0; + combined_verts[v1] += combined_verts[v2] + 1; + if (do_shell) { numNewLoops -= edge_adj_faces_len[i] * 2; } @@ -429,6 +440,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, } MEM_freeN(face_singularity); + MEM_freeN(combined_verts); } /* Create vert_adj_edges for verts. */ @@ -668,7 +680,7 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, const uint v1 = vm[ed->v1]; const uint v2 = vm[ed->v2]; if (edge_adj_faces_len[i] > 0) { - sub_v3_v3v3(edgedir, orig_mvert[v2].co, orig_mvert[v1].co); + sub_v3_v3v3(edgedir, orig_mvert_co[v2], orig_mvert_co[v1]); mul_v3_fl(edgedir, 1.0f / orig_edge_lengths[i]); OldEdgeFaceRef *adj_faces = edge_adj_faces[i]; @@ -1496,8 +1508,9 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, ml_prev = ml; ml = ml_next; } - angle = angle_v3v3v3( - orig_mvert[vm[ml_prev->v]].co, mv->co, orig_mvert[vm[ml_next->v]].co); + angle = angle_v3v3v3(orig_mvert_co[vm[ml_prev->v]], + orig_mvert_co[i], + orig_mvert_co[vm[ml_next->v]]); if (face->reversed) { total_angle_back += angle * ofs * ofs; } @@ -1591,7 +1604,8 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, uint k; for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) { MEdge *e = orig_medge + (*edge_ptr)->old_edge; - sub_v3_v3v3(tmp, orig_mvert[vm[e->v1] == i ? e->v2 : e->v1].co, mv->co); + sub_v3_v3v3( + tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]); add_v3_v3(move_nor, tmp); } if (k == 1) { @@ -1613,10 +1627,12 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, MEdge *e1_edge = orig_medge + g->edges[g->edges_len - 1]->old_edge; float e0[3]; float e1[3]; - sub_v3_v3v3( - e0, orig_mvert[vm[e0_edge->v1] == i ? e0_edge->v2 : e0_edge->v1].co, mv->co); - sub_v3_v3v3( - e1, orig_mvert[vm[e1_edge->v1] == i ? e1_edge->v2 : e1_edge->v1].co, mv->co); + sub_v3_v3v3(e0, + orig_mvert_co[vm[e0_edge->v1] == i ? e0_edge->v2 : e0_edge->v1], + orig_mvert_co[i]); + sub_v3_v3v3(e1, + orig_mvert_co[vm[e1_edge->v1] == i ? e1_edge->v2 : e1_edge->v1], + orig_mvert_co[i]); if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) { cross_v3_v3v3(constr_nor, e0, e1); } @@ -1702,16 +1718,17 @@ Mesh *MOD_solidify_nonmanifold_applyModifier(ModifierData *md, } } mul_v3_fl(nor, scalar_vgroup); - add_v3_v3v3(g->co, nor, mv->co); + add_v3_v3v3(g->co, nor, orig_mvert_co[i]); } else { - copy_v3_v3(g->co, mv->co); + copy_v3_v3(g->co, orig_mvert_co[i]); } } } } } + MEM_freeN(orig_mvert_co); if (null_faces) { MEM_freeN(null_faces); } -- cgit v1.2.3 From ad317a5ffdcfdfed9ef3f82ee82fd2dbd41e2bc9 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 13 Apr 2020 19:12:48 +0200 Subject: Fix T75676: Inconsistent "Only selected" GP layers in Dope Sheet The selection was not checking all modes. --- source/blender/editors/animation/anim_filter.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 8bdf1ef7684..4dc0bef3a1b 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1880,13 +1880,12 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, } } - /* check selection and object type filters only for Object mode */ - if (ob->mode == OB_MODE_OBJECT) { - if ((ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & BASE_SELECTED))) { - /* only selected should be shown */ - continue; - } + /* check selection and object type filters */ + if ((ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & BASE_SELECTED))) { + /* only selected should be shown */ + continue; } + /* check if object belongs to the filtering group if option to filter * objects by the grouped status is on * - used to ease the process of doing multiple-character choreographies -- cgit v1.2.3 From 9ce83acba47e5028e98949058c1fedc50385f6fd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Apr 2020 10:41:05 +1000 Subject: Cleanup: remove redundant include, clang-format --- source/blender/blenkernel/BKE_sequencer.h | 5 ++++- source/blender/editors/include/ED_util_imbuf.h | 6 +++--- source/blender/makesrna/intern/rna_modifier.c | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index cb95bacba4c..9268fdf1391 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -435,7 +435,10 @@ void BKE_sequence_invalidate_cache_composite(struct Scene *scene, struct Sequenc void BKE_sequence_invalidate_dependent(struct Scene *scene, struct Sequence *seq); void BKE_sequence_invalidate_scene_strips(struct Main *bmain, struct Scene *scene_target); void BKE_sequence_invalidate_movieclip_strips(struct Main *bmain, struct MovieClip *clip_target); -void BKE_sequence_invalidate_cache_in_range(struct Scene *scene, struct Sequence *seq, struct Sequence *range_mask, int invalidate_types); +void BKE_sequence_invalidate_cache_in_range(struct Scene *scene, + struct Sequence *seq, + struct Sequence *range_mask, + int invalidate_types); void BKE_sequencer_update_sound_bounds_all(struct Scene *scene); void BKE_sequencer_update_sound_bounds(struct Scene *scene, struct Sequence *seq); diff --git a/source/blender/editors/include/ED_util_imbuf.h b/source/blender/editors/include/ED_util_imbuf.h index 64349556744..76171383b49 100644 --- a/source/blender/editors/include/ED_util_imbuf.h +++ b/source/blender/editors/include/ED_util_imbuf.h @@ -25,17 +25,17 @@ #define __ED_UTIL_IMBUF_H__ #include "BLI_compiler_attrs.h" - -#include "DNA_screen_types.h" +#include "BLI_sys_types.h" #ifdef __cplusplus extern "C" { #endif +struct ARegion; struct Main; struct bContext; -struct wmOperator; struct wmEvent; +struct wmOperator; /* ed_util_imbuf.c */ void ED_imbuf_sample_draw(const struct bContext *C, struct ARegion *region, void *arg_info); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 2c4818eb972..5fc91622eba 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -4528,7 +4528,8 @@ static void rna_def_modifier_solidify(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "merge_tolerance"); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4); - RNA_def_property_ui_text(prop, "Merge Threshold", "Distance within which degenerated geometry is merged"); + RNA_def_property_ui_text( + prop, "Merge Threshold", "Distance within which degenerated geometry is merged"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); } -- cgit v1.2.3 From 5f059c8751379edaabdffb68af7f27979c1c12f6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Apr 2020 10:41:21 +1000 Subject: Cleanup: spelling --- source/blender/blenkernel/intern/lib_id.c | 2 +- source/blender/depsgraph/intern/depsgraph_eval.cc | 2 +- source/blender/editors/interface/interface.c | 5 ++--- source/blender/editors/space_node/node_relationships.c | 4 ++-- source/blender/editors/transform/transform_convert_armature.c | 6 +++--- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 9e4f1dda682..80f29a55b28 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1724,7 +1724,7 @@ static void library_make_local_copying_check(ID *id, * relation we want to check is in the other way around. */ if (entry->usage_flag & IDWALK_CB_LOOPBACK) { #ifndef NDEBUG - /* Some debug checks to ensure we explicitely are aware of all 'loopback' cases, since those + /* Some debug checks to ensure we explicitly are aware of all 'loop-back' cases, since those * may not always be manageable in the same way... */ switch (GS(par_id->name)) { case ID_OB: diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index 7e7ab07825f..9251d975125 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -20,7 +20,7 @@ /** \file * \ingroup depsgraph * - * Evaluation engine entrypoints for Depsgraph Engine. + * Evaluation engine entry-points for Depsgraph Engine. */ #include "MEM_guardedalloc.h" diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 18666daa8b8..2b674686ffa 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -1299,9 +1299,8 @@ static bool ui_but_event_property_operator_string(const bContext *C, } else if (GS(id->name) == ID_SCE) { if (RNA_struct_is_a(ptr->type, &RNA_ToolSettings)) { - /* toolsettings property - * NOTE: toolsettings is usually accessed directly (i.e. not through scene) - */ + /* Tool-settings property: + * NOTE: tool-settings is usually accessed directly (i.e. not through scene). */ data_path = RNA_path_from_ID_to_property(ptr, prop); } else { diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 8f8f945a600..8c2f79109f6 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -1622,8 +1622,8 @@ static void node_parent_offset_apply(NodeInsertOfsData *data, bNode *parent, con node_offset_apply(parent, offset_x); - /* flag all childs as offset to prevent them from being offset - * separately (they've already moved with the parent) */ + /* Flag all children as offset to prevent them from being offset + * separately (they've already moved with the parent). */ for (node = data->ntree->nodes.first; node; node = node->next) { if (nodeIsChildOf(parent, node)) { /* NODE_TEST is used to flag nodes that shouldn't be offset (again) */ diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 481198ea88c..0106f4f41c7 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -231,9 +231,9 @@ static void add_pose_transdata( data->flag |= CONSTRAINT_IK_AUTO; - /* Add a temporary auto IK constraint here, as we will only temporarly active this targetless - * bone during transform. (Targetless IK constraints are treated as if they are disabled - * unless they are transformed) */ + /* Add a temporary auto IK constraint here, as we will only temporarily active this + * targetless bone during transform. (Targetless IK constraints are treated as if they are + * disabled unless they are transformed). */ add_temporary_ik_constraint(pchan, data); Main *bmain = CTX_data_main(t->context); update_deg_with_temporary_ik(bmain, ob); -- cgit v1.2.3 From 9e0b44aae941d0d175a072c12423b0db094c4c3e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Apr 2020 10:44:46 +1000 Subject: Cleanup: ed_util_imbuf sections --- source/blender/editors/util/ed_util_imbuf.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index 0c4ddc2b809..132a63a8249 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -53,7 +53,9 @@ /* Own define. */ #include "ED_util_imbuf.h" -/* ********* Pixel sample operator ********* */ +/* -------------------------------------------------------------------- */ +/** \name Image Pixel Sample Struct (Operator Custom Data) + * \{ */ typedef struct ImageSampleInfo { ARegionType *art; @@ -80,6 +82,8 @@ typedef struct ImageSampleInfo { int use_default_view; } ImageSampleInfo; +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Image Pixel Sample * \{ */ @@ -155,6 +159,10 @@ static void image_sample_rect_color_float(ImBuf *ibuf, const rcti *rect, float r /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Image Pixel Sample (Internal Utilities) + * \{ */ + static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event) { SpaceImage *sima = CTX_wm_space_image(C); @@ -387,6 +395,14 @@ static void ed_imbuf_sample_apply(bContext *C, wmOperator *op, const wmEvent *ev } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Pixel Sample (Public Operator Callback) + * + * Callbacks for the sample operator, used by sequencer and image spaces. + * \{ */ + void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info) { ImageSampleInfo *info = arg_info; @@ -551,3 +567,5 @@ bool ED_imbuf_sample_poll(bContext *C) return false; } + +/** \} */ -- cgit v1.2.3 From b2c2d7b7f13a0c033853dcc87b49dbda4ba737af Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 30 Mar 2020 09:35:01 +0200 Subject: Sculpt: Implement undo of Apply Base during sculpt session The idea is to push both base mesh geometry and PBVH coordinates so it is possible to undo everything without loosing data which was not flushed from sculpt session to base mesh. It is possible do memory optimization to avoid push custom data layers which are not touched by operator, but before doing that better to ensure this is a correct and working approach. Differential Revision: https://developer.blender.org/D7381 --- source/blender/editors/include/ED_sculpt.h | 5 ++ source/blender/editors/object/object_modifier.c | 7 +- .../blender/editors/sculpt_paint/sculpt_intern.h | 1 + source/blender/editors/sculpt_paint/sculpt_undo.c | 94 +++++++++++++++++++++- 4 files changed, 105 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 26871cf8dd0..e61c7be5216 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -54,6 +54,11 @@ void ED_sculpt_undosys_type(struct UndoType *ut); void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name); void ED_sculpt_undo_geometry_end(struct Object *ob); +/* Undo for changes happening on a base mesh for multires sculpting. + * if there is no multires sculpt active regular undo is used. */ +void ED_sculpt_undo_push_multires_mesh_begin(struct bContext *C, const char *str); +void ED_sculpt_undo_push_multires_mesh_end(struct bContext *C, const char *str); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 2babf27eb61..a24f3ba2269 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -86,6 +86,7 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_sculpt.h" #include "WM_api.h" #include "WM_types.h" @@ -1697,8 +1698,12 @@ static int multires_base_apply_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + ED_sculpt_undo_push_multires_mesh_begin(C, op->type->name); + multiresModifier_base_apply(depsgraph, object, mmd); + ED_sculpt_undo_push_multires_mesh_end(C, op->type->name); + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object); @@ -1726,7 +1731,7 @@ void OBJECT_OT_multires_base_apply(wmOperatorType *ot) ot->exec = multires_base_apply_exec; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL; edit_modifier_properties(ot); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index e2fd54596e7..b379c1ab8af 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -478,6 +478,7 @@ typedef struct SculptUndoNode { * the object when undoing the operation * * Modified geometry is stored after the modification and is used to redo the modification. */ + bool geometry_clear_pbvh; SculptUndoNodeGeometry geometry_original; SculptUndoNodeGeometry geometry_modified; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 340f7191b95..c07ebf790d4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -55,6 +55,9 @@ #include "BKE_subsurf.h" #include "BKE_undo_system.h" +// XXX: Ideally should be no direct call to such low level things. +#include "BKE_subdiv_eval.h" + #include "DEG_depsgraph.h" #include "WM_api.h" @@ -552,7 +555,9 @@ static void sculpt_undo_geometry_free_data(SculptUndoNodeGeometry *geometry) static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object) { - SCULPT_pbvh_clear(object); + if (unode->geometry_clear_pbvh) { + SCULPT_pbvh_clear(object); + } if (unode->applied) { sculpt_undo_geometry_restore_data(&unode->geometry_modified, object); @@ -740,6 +745,10 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } + if (subdiv_ccg != NULL) { + BKE_subdiv_eval_refine_from_mesh(subdiv_ccg->subdiv, ob->data, NULL); + } + if (update || rebuild) { bool tag_update = false; /* We update all nodes still, should be more clever, but also @@ -1079,6 +1088,7 @@ static SculptUndoNode *sculpt_undo_geometry_push(Object *object, SculptUndoType { SculptUndoNode *unode = sculpt_undo_find_or_alloc_node_type(object, type); unode->applied = false; + unode->geometry_clear_pbvh = true; SculptUndoNodeGeometry *geometry = sculpt_undo_geometry_get(unode); sculpt_undo_geometry_store_data(geometry, object); @@ -1510,3 +1520,85 @@ static UndoSculpt *sculpt_undo_get_nodes(void) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Undo for changes happening on a base mesh for multires sculpting. + * + * Use this for multires operators which changes base mesh and which are to be + * possible. Example of such operators is Apply Base. + * + * Usage: + * + * static int operator_exec((bContext *C, wmOperator *op) { + * + * ED_sculpt_undo_push_mixed_begin(C, op->type->name); + * // Modify base mesh. + * ED_sculpt_undo_push_mixed_end(C, op->type->name); + * + * return OPERATOR_FINISHED; + * } + * + * If object is not in sculpt mode or sculpt does not happen on multires then + * regular ED_undo_push() is used. + * * + * \{ */ + +static bool sculpt_undo_use_multires_mesh(bContext *C) +{ + if (BKE_paintmode_get_active_from_context(C) != PAINT_MODE_SCULPT) { + return false; + } + + Object *object = CTX_data_active_object(C); + SculptSession *sculpt_session = object->sculpt; + + return sculpt_session->multires.active; +} + +static void sculpt_undo_push_all_grids(Object *object) +{ + SculptSession *ss = object->sculpt; + PBVHNode **nodes; + int totnodes; + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); + for (int i = 0; i < totnodes; i++) { + SculptUndoNode *unode = SCULPT_undo_push_node(object, nodes[i], SCULPT_UNDO_COORDS); + unode->node = NULL; + } + + MEM_SAFE_FREE(nodes); +} + +void ED_sculpt_undo_push_multires_mesh_begin(bContext *C, const char *str) +{ + if (!sculpt_undo_use_multires_mesh(C)) { + return; + } + + Object *object = CTX_data_active_object(C); + + SCULPT_undo_push_begin(str); + + SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY); + geometry_unode->geometry_clear_pbvh = false; + + sculpt_undo_push_all_grids(object); +} + +void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str) +{ + if (!sculpt_undo_use_multires_mesh(C)) { + ED_undo_push(C, str); + return; + } + + Object *object = CTX_data_active_object(C); + + SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY); + geometry_unode->geometry_clear_pbvh = false; + + SCULPT_undo_push_end(); +} + +/** \} */ -- cgit v1.2.3 From 0fa7e1efbe244174e74dcbafbe50d7003a907ef6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Apr 2020 18:30:27 +1000 Subject: UI: correct menu used for dope-sheet --- source/blender/editors/interface/interface_templates.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 98d5bae0ff1..31417844347 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6952,7 +6952,7 @@ static struct MenuSearch_Data *menu_items_from_ui_create(bContext *C, SPACE_MENU_MAP(SPACE_INFO, "INFO_MT_editor_menus"); SPACE_MENU_MAP(SPACE_SEQ, "SEQUENCER_MT_editor_menus"); SPACE_MENU_MAP(SPACE_TEXT, "TEXT_MT_editor_menus"); - SPACE_MENU_MAP(SPACE_ACTION, "ACTION_MT_editor_menus"); + SPACE_MENU_MAP(SPACE_ACTION, "DOPESHEET_MT_editor_menus"); SPACE_MENU_MAP(SPACE_NLA, "NLA_MT_editor_menus"); SPACE_MENU_MAP(SPACE_NODE, "NODE_MT_editor_menus"); SPACE_MENU_MAP(SPACE_CONSOLE, "CONSOLE_MT_editor_menus"); -- cgit v1.2.3 From 3bef5d15d8c0a3a23922c0658ea4e0654199f0b4 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Apr 2020 18:34:07 +1000 Subject: UI: add spin to extrude menu This tool wasn't accessible anywhere in the interface (besides the interactive tool). Add to extrude since it's a kind of extrusion. --- release/scripts/startup/bl_ui/space_view3d.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 167091815b4..f38bbb46f85 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3879,6 +3879,8 @@ class VIEW3D_MT_edit_mesh_extrude(Menu): return menu def draw(self, context): + from math import pi + layout = self.layout layout.operator_context = 'INVOKE_REGION_WIN' @@ -3888,6 +3890,7 @@ class VIEW3D_MT_edit_mesh_extrude(Menu): layout.separator() layout.operator("mesh.extrude_repeat") + layout.operator("mesh.spin").angle = pi * 2 class VIEW3D_MT_edit_mesh_vertices(Menu): -- cgit v1.2.3 From 571646ebc175877225c26a3dcf8869691cf70ba7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Apr 2020 18:41:23 +1000 Subject: Cleanup: pass font drawing x/y offset arguments as int's Internally these values are ints. --- source/blender/editors/include/UI_interface.h | 4 ++-- source/blender/editors/interface/interface_style.c | 6 +++--- source/blender/editors/interface/interface_widgets.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index eb134646649..1133f9e72c4 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2415,8 +2415,8 @@ void UI_fontstyle_draw_ex(const struct uiFontStyle *fs, const uchar col[4], const struct uiFontStyleDraw_Params *fs_params, size_t len, - float *r_xofs, - float *r_yofs); + int *r_xofs, + int *r_yofs); void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str, diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 63fc7825b26..a67d4f4ce83 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -147,8 +147,8 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, const uchar col[4], const struct uiFontStyleDraw_Params *fs_params, size_t len, - float *r_xofs, - float *r_yofs) + int *r_xofs, + int *r_yofs) { int xofs = 0, yofs; int font_flag = BLF_CLIPPING; @@ -210,7 +210,7 @@ void UI_fontstyle_draw(const uiFontStyle *fs, const uchar col[4], const struct uiFontStyleDraw_Params *fs_params) { - float xofs, yofs; + int xofs, yofs; UI_fontstyle_draw_ex(fs, rect, str, col, fs_params, BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs); } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index fa4a0a1e07d..88bc4c38fd3 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2300,7 +2300,7 @@ static void widget_draw_text(const uiFontStyle *fstyle, if (!use_right_only) { /* for underline drawing */ - float font_xofs, font_yofs; + int font_xofs, font_yofs; int drawlen = (drawstr_left_len == INT_MAX) ? strlen(drawstr + but->ofs) : (drawstr_left_len - but->ofs); @@ -2342,7 +2342,7 @@ static void widget_draw_text(const uiFontStyle *fstyle, ul_advance = BLF_width(fstyle->uifont_id, fixedbuf, ul_index) + (1.0f * UI_DPI_FAC); BLF_position(fstyle->uifont_id, - rect->xmin + font_xofs + ul_advance, + rect->xmin + font_xofs + (int)ul_advance, rect->ymin + font_yofs, 0.0f); BLF_color4ubv(fstyle->uifont_id, wcol->text); -- cgit v1.2.3 From e6d9d5dcc18f3ab55c31abc6bfb2010081538422 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 14 Apr 2020 18:46:13 +1000 Subject: UI: improve menu search with dimmed menu prefix - Show dimmed text for the menu entries leading up to the menu item. - Show icons between the menu text and menu item. - Use unicode right pointing triangle instead of arrow. --- source/blender/editors/include/UI_interface.h | 5 +- source/blender/editors/interface/interface.c | 7 ++- .../blender/editors/interface/interface_intern.h | 4 +- .../blender/editors/interface/interface_layout.c | 1 + .../editors/interface/interface_region_search.c | 57 ++++++++++++++++++---- source/blender/editors/interface/interface_style.c | 7 +-- .../editors/interface/interface_templates.c | 30 +++++++++--- .../blender/editors/interface/interface_widgets.c | 35 +++++++++---- source/blender/editors/space_node/node_select.c | 2 +- .../editors/space_outliner/outliner_tools.c | 2 +- 10 files changed, 115 insertions(+), 35 deletions(-) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 1133f9e72c4..774cf8c509f 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -49,6 +49,7 @@ struct PanelType; struct PointerRNA; struct PropertyRNA; struct ReportList; +struct ResultBLF; struct ScrArea; struct bContext; struct bContextStore; @@ -1576,6 +1577,7 @@ void UI_but_func_search_set(uiBut *but, void *arg, uiButSearchArgFreeFunc search_arg_free_func, uiButHandleFunc bfunc, + const char *search_sep_string, void *active); /* height in pixels, it's using hardcoded values still */ int UI_searchbox_size_y(void); @@ -2416,7 +2418,8 @@ void UI_fontstyle_draw_ex(const struct uiFontStyle *fs, const struct uiFontStyleDraw_Params *fs_params, size_t len, int *r_xofs, - int *r_yofs); + int *r_yofs, + struct ResultBLF *r_info); void UI_fontstyle_draw(const struct uiFontStyle *fs, const struct rcti *rect, const char *str, diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 2b674686ffa..9ec660a9714 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -6332,7 +6332,9 @@ uiBut *uiDefSearchBut(uiBlock *block, /** * \param search_func, bfunc: both get it as \a arg. * \param arg: user value, - * \param active: when set, button opens with this item visible and selected. + * \param active: when set, button opens with this item visible and selected. + * \param separator_string: when not NULL, this string is used as a separator, + * showing the icon and highlighted text after the last instance of this string. */ void UI_but_func_search_set(uiBut *but, uiButSearchCreateFunc search_create_func, @@ -6340,6 +6342,7 @@ void UI_but_func_search_set(uiBut *but, void *arg, uiButSearchArgFreeFunc search_arg_free_func, uiButHandleFunc bfunc, + const char *search_sep_string, void *active) { /* needed since callers don't have access to internal functions @@ -6358,6 +6361,7 @@ void UI_but_func_search_set(uiBut *but, but->search_arg = arg; but->search_arg_free_func = search_arg_free_func; + but->search_sep_string = search_sep_string; if (bfunc) { #ifdef DEBUG @@ -6467,6 +6471,7 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block, but, NULL, operator_enum_call_cb, + NULL, NULL); but->optype = ot; diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 5573d9b2edb..9142aebd1ef 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -205,6 +205,7 @@ struct uiBut { uiButSearchFunc search_func; void *search_arg; uiButSearchArgFreeFunc search_arg_free_func; + const char *search_sep_string; uiButHandleRenameFunc rename_func; void *rename_arg1; @@ -851,7 +852,8 @@ void ui_draw_menu_item(const struct uiFontStyle *fstyle, const char *name, int iconid, int state, - bool use_sep); + bool use_sep, + int *r_name_width); void ui_draw_preview_item( const struct uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 92779c83d9a..cbea32f179a 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -2633,6 +2633,7 @@ void ui_but_add_search( coll_search, ui_rna_collection_search_free_cb, NULL, + NULL, NULL); } else if (but->type == UI_BTYPE_SEARCH_MENU) { diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 48779fd86dc..e2c87891169 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -28,6 +28,7 @@ #include #include +#include "DNA_ID.h" #include "MEM_guardedalloc.h" #include "DNA_userdef_types.h" @@ -89,9 +90,14 @@ typedef struct uiSearchboxData { bool noback; /** draw thumbnail previews, rather than list */ bool preview; - /** use the UI_SEP_CHAR char for splitting shortcuts (good for operators, bad for data) */ + /** Use the #UI_SEP_CHAR char for splitting shortcuts (good for operators, bad for data). */ bool use_sep; int prv_rows, prv_cols; + /** + * Show the active icon and text after the last instance of this string. + * Used so we can show leading text to menu items less prominently (not related to 'use_sep'). + */ + const char *sep_string; } uiSearchboxData; #define SEARCH_ITEMS 10 @@ -465,19 +471,50 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) } } else { + const int search_sep_len = data->sep_string ? strlen(data->sep_string) : 0; /* draw items */ for (a = 0; a < data->items.totitem; a++) { const int state = ((a == data->active) ? UI_ACTIVE : 0) | data->items.states[a]; + char *name = data->items.names[a]; + int icon = data->items.icons[a]; + char *name_sep_test = NULL; ui_searchbox_butrect(&rect, data, a); /* widget itself */ - ui_draw_menu_item(&data->fstyle, - &rect, - data->items.names[a], - data->items.icons[a], - state, - data->use_sep); + if ((search_sep_len == 0) || + !(name_sep_test = strstr(data->items.names[a], data->sep_string))) { + + /* Simple menu item. */ + ui_draw_menu_item(&data->fstyle, &rect, name, icon, state, data->use_sep, NULL); + } + else { + /* Split menu item, faded text before the separator. */ + char *name_sep = NULL; + do { + name_sep = name_sep_test; + name_sep_test = strstr(name_sep + search_sep_len, data->sep_string); + } while (name_sep_test != NULL); + + name_sep += search_sep_len; + const char name_sep_prev = *name_sep; + *name_sep = '\0'; + int name_width = 0; + ui_draw_menu_item( + &data->fstyle, &rect, name, 0, state | UI_BUT_INACTIVE, false, &name_width); + *name_sep = name_sep_prev; + rect.xmin += name_width; + rect.xmin += UI_UNIT_X / 4; + + if (icon == ICON_BLANK1) { + icon = ICON_NONE; + rect.xmin -= UI_DPI_ICON_SIZE / 4; + } + + /* The previous menu item draws the active selection. */ + ui_draw_menu_item( + &data->fstyle, &rect, name_sep, icon, state & ~UI_ACTIVE, data->use_sep, NULL); + } } /* indicate more */ if (data->items.more) { @@ -566,6 +603,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiBut *but if (but->optype != NULL || (but->drawflag & UI_BUT_HAS_SHORTCUT) != 0) { data->use_sep = true; } + data->sep_string = but->search_sep_string; /* compute position */ if (but->block->flag & UI_BLOCK_SEARCH_MENU) { @@ -762,9 +800,10 @@ static void ui_searchbox_region_draw_cb__operator(const bContext *UNUSED(C), ARe CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, text_pre), data->items.icons[a], state, - false); + false, + NULL); ui_draw_menu_item( - &data->fstyle, &rect_post, data->items.names[a], 0, state, data->use_sep); + &data->fstyle, &rect_post, data->items.names[a], 0, state, data->use_sep, NULL); } } /* indicate more */ diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index a67d4f4ce83..d14c9e69e55 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -148,7 +148,8 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, const struct uiFontStyleDraw_Params *fs_params, size_t len, int *r_xofs, - int *r_yofs) + int *r_yofs, + struct ResultBLF *r_info) { int xofs = 0, yofs; int font_flag = BLF_CLIPPING; @@ -196,7 +197,7 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, BLF_position(fs->uifont_id, rect->xmin + xofs, rect->ymin + yofs, 0.0f); BLF_color4ubv(fs->uifont_id, col); - BLF_draw(fs->uifont_id, str, len); + BLF_draw_ex(fs->uifont_id, str, len, r_info); BLF_disable(fs->uifont_id, font_flag); @@ -212,7 +213,7 @@ void UI_fontstyle_draw(const uiFontStyle *fs, { int xofs, yofs; - UI_fontstyle_draw_ex(fs, rect, str, col, fs_params, BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs); + UI_fontstyle_draw_ex(fs, rect, str, col, fs_params, BLF_DRAW_STR_DUMMY_MAX, &xofs, &yofs, NULL); } /* drawn same as above, but at 90 degree angle */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 31417844347..f730e4c0e52 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -287,8 +287,14 @@ static uiBlock *template_common_search_menu(const bContext *C, 0, ""); } - UI_but_func_search_set( - but, ui_searchbox_create_generic, search_func, search_arg, NULL, handle_func, active_item); + UI_but_func_search_set(but, + ui_searchbox_create_generic, + search_func, + search_arg, + NULL, + handle_func, + NULL, + active_item); UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); UI_block_direction_set(block, UI_DIR_DOWN); @@ -6660,8 +6666,14 @@ static void operator_search_cb(const bContext *C, void UI_but_func_operator_search(uiBut *but) { - UI_but_func_search_set( - but, ui_searchbox_create_operator, operator_search_cb, NULL, false, operator_call_cb, NULL); + UI_but_func_search_set(but, + ui_searchbox_create_operator, + operator_search_cb, + NULL, + false, + operator_call_cb, + NULL, + NULL); } void uiTemplateOperatorSearch(uiLayout *layout) @@ -6684,6 +6696,9 @@ void uiTemplateOperatorSearch(uiLayout *layout) /** \name Menu Search Template * \{ */ +/* Unicode arrow. */ +#define MENU_SEP "\xe2\x96\xb6" + struct MenuSearch_Parent { struct MenuSearch_Parent *parent; MenuType *parent_mt; @@ -7127,9 +7142,6 @@ static struct MenuSearch_Data *menu_items_from_ui_create(bContext *C, /* NOTE: currently this builds the full path for each menu item, * that could be moved into the parent menu. */ - /* Unicode arrow. */ -#define MENU_SEP "\xe2\x86\x92" - /* Set names as full paths. */ LISTBASE_FOREACH (struct MenuSearch_Item *, item, &data->items) { if (item->menu_parent != NULL) { @@ -7176,7 +7188,6 @@ static struct MenuSearch_Data *menu_items_from_ui_create(bContext *C, BLI_dynstr_clear(dyn_str); } BLI_dynstr_free(dyn_str); -#undef MENU_SEP /* Finally sort menu items. * @@ -7309,6 +7320,7 @@ void UI_but_func_menu_search(uiBut *but) data, menu_items_from_ui_destroy, menu_call_fn, + MENU_SEP, NULL); } @@ -7326,6 +7338,8 @@ void uiTemplateMenuSearch(uiLayout *layout) UI_but_func_menu_search(but); } +#undef MENU_SEP + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 88bc4c38fd3..86a5fd5dabb 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2315,7 +2315,8 @@ static void widget_draw_text(const uiFontStyle *fstyle, }, drawlen, &font_xofs, - &font_yofs); + &font_yofs, + NULL); if (but->menu_key != '\0') { char fixedbuf[128]; @@ -5267,8 +5268,13 @@ void ui_draw_tooltip_background(const uiStyle *UNUSED(style), uiBlock *UNUSED(bl /* helper call to draw a menu item without button */ /* state: UI_ACTIVE or 0 */ -void ui_draw_menu_item( - const uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state, bool use_sep) +void ui_draw_menu_item(const uiFontStyle *fstyle, + rcti *rect, + const char *name, + int iconid, + int state, + bool use_sep, + int *r_name_width) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM); rcti _rect = *rect; @@ -5318,13 +5324,22 @@ void ui_draw_menu_item( UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0'); } - UI_fontstyle_draw(fstyle, - rect, - drawstr, - wt->wcol.text, - &(struct uiFontStyleDraw_Params){ - .align = UI_STYLE_TEXT_LEFT, - }); + int xofs = 0, yofs = 0; + struct ResultBLF info; + UI_fontstyle_draw_ex(fstyle, + rect, + drawstr, + wt->wcol.text, + &(struct uiFontStyleDraw_Params){ + .align = UI_STYLE_TEXT_LEFT, + }, + BLF_DRAW_STR_DUMMY_MAX, + &xofs, + &yofs, + &info); + if (r_name_width != NULL) { + *r_name_width = xofs + info.width; + } } /* part text right aligned */ diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 38ec855e845..50c32da4b5a 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -1179,7 +1179,7 @@ static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_op) 0, 0, ""); - UI_but_func_search_set(but, NULL, node_find_cb, op->type, NULL, node_find_call_cb, NULL); + UI_but_func_search_set(but, NULL, node_find_cb, op->type, NULL, node_find_call_cb, NULL, NULL); UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); /* fake button, it holds space for search items */ diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 34ca2267b7a..fe62272614f 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -590,7 +590,7 @@ static uiBlock *merged_element_search_menu(bContext *C, ARegion *region, void *d but = uiDefSearchBut( block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, menu_width, UI_UNIT_Y, 0, 0, ""); UI_but_func_search_set( - but, NULL, merged_element_search_cb, data, NULL, merged_element_search_call_cb, NULL); + but, NULL, merged_element_search_cb, data, NULL, merged_element_search_call_cb, NULL, NULL); UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT); /* Fake button to hold space for search items */ -- cgit v1.2.3 From 47084bac9f3c4c95b6fa3e3c3d5b1e661b2bec47 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 13 Apr 2020 17:38:34 +0200 Subject: Fix T75542: toggling modifier visibility not working correct with undo speedup The problem was that in direct_link_id_restore_recalc, recalc_undo_accumulated should contain the changes from the target state to the current state. However it had already been cleared at that point, to start accumulating changes up to the next undo push. Delaying the clear of this flag seems like the obvious solution, but it's hard to find the right place for that (if there is one). Instead this splits up the flag into two separate variables. Reviewed By: mont29 Differential Revision: https://developer.blender.org/D7402 --- source/blender/blenloader/intern/readfile.c | 10 +++---- source/blender/blenloader/intern/writefile.c | 35 +++++++++++++++--------- source/blender/depsgraph/intern/depsgraph_tag.cc | 2 +- source/blender/editors/undo/memfile_undo.c | 6 ++-- source/blender/makesdna/DNA_ID.h | 15 ++++++---- 5 files changed, 40 insertions(+), 28 deletions(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 799330a07bf..168b4e01e8d 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2845,11 +2845,11 @@ static int direct_link_id_restore_recalc(const FileData *fd, * that we need to perform again. */ if (fd->undo_direction < 0) { /* Undo: tags from target to the current state. */ - recalc |= id_current->recalc_undo_accumulated; + recalc |= id_current->recalc_up_to_undo_push; } else { /* Redo: tags from current to the target state. */ - recalc |= id_target->recalc_undo_accumulated; + recalc |= id_target->recalc_up_to_undo_push; } } @@ -2880,11 +2880,11 @@ static void direct_link_id_common(FileData *fd, ID *id, ID *id_old, const int ta * the version the file has been saved with. */ if (fd->memfile == NULL) { id->recalc = 0; - id->recalc_undo_accumulated = 0; + id->recalc_after_undo_push = 0; } else if ((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0) { id->recalc = direct_link_id_restore_recalc(fd, id, id_old, false); - id->recalc_undo_accumulated = 0; + id->recalc_after_undo_push = 0; } /* Link direct data of overrides. */ @@ -9555,7 +9555,7 @@ static void read_libblock_undo_restore_identical( /* Recalc flags, mostly these just remain as they are. */ id_old->recalc |= direct_link_id_restore_recalc_exceptions(id_old); - id_old->recalc_undo_accumulated = 0; + id_old->recalc_after_undo_push = 0; /* As usual, proxies require some special love... * In `blo_clear_proxy_pointers_from_lib()` we clear all `proxy_from` pointers to local IDs, for diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index fc6ec80e09a..ee5e6f4610a 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1133,11 +1133,6 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree) for (sock = ntree->outputs.first; sock; sock = sock->next) { write_node_socket_interface(wd, sock); } - - /* Clear the accumulated recalc flags in case of undo step saving. */ - if (wd->use_memfile) { - ntree->id.recalc_undo_accumulated = 0; - } } /** @@ -2454,11 +2449,6 @@ static void write_collection_nolib(WriteData *wd, Collection *collection) LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { writestruct(wd, DATA, CollectionChild, 1, child); } - - /* Clear the accumulated recalc flags in case of undo step saving. */ - if (wd->use_memfile) { - collection->id.recalc_undo_accumulated = 0; - } } static void write_collection(WriteData *wd, Collection *collection, const void *id_address) @@ -4069,6 +4059,28 @@ static bool write_file_handle(Main *mainvar, BKE_lib_override_library_operations_store_start(bmain, override_storage, id); } + if (wd->use_memfile) { + /* Record the changes that happened up to this undo push in + * recalc_up_to_undo_push, and clear recalc_after_undo_push again + * to start accumulating for the next undo push. */ + id->recalc_up_to_undo_push = id->recalc_after_undo_push; + id->recalc_after_undo_push = 0; + + bNodeTree *nodetree = ntreeFromID(id); + if (nodetree != NULL) { + nodetree->id.recalc_up_to_undo_push = nodetree->id.recalc_after_undo_push; + nodetree->id.recalc_after_undo_push = 0; + } + if (GS(id->name) == ID_SCE) { + Scene *scene = (Scene *)id; + if (scene->master_collection != NULL) { + scene->master_collection->id.recalc_up_to_undo_push = + scene->master_collection->id.recalc_after_undo_push; + scene->master_collection->id.recalc_after_undo_push = 0; + } + } + } + memcpy(id_buffer, id, idtype_struct_size); ((ID *)id_buffer)->tag = 0; @@ -4206,9 +4218,6 @@ static bool write_file_handle(Main *mainvar, /* Very important to do it after every ID write now, otherwise we cannot know whether a * specific ID changed or not. */ mywrite_flush(wd); - - /* Clear the accumulated recalc flags in case of undo step saving. */ - id->recalc_undo_accumulated = 0; } } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 627a93b5869..1c56808ea82 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -625,7 +625,7 @@ void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source) /* Accumulate all tags for an ID between two undo steps, so they can be * replayed for undo. */ - id->recalc_undo_accumulated |= deg_recalc_flags_effective(NULL, flag); + id->recalc_after_undo_push |= deg_recalc_flags_effective(NULL, flag); } void graph_id_tag_update( diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c index ebd5b2272b1..9954cf85157 100644 --- a/source/blender/editors/undo/memfile_undo.c +++ b/source/blender/editors/undo/memfile_undo.c @@ -234,15 +234,15 @@ static void memfile_undosys_step_decode(struct bContext *C, /* We only start accumulating from this point, any tags set up to here * are already part of the current undo state. This is done in a second * loop because DEG_id_tag_update may set tags on other datablocks. */ - id->recalc_undo_accumulated = 0; + id->recalc_after_undo_push = 0; bNodeTree *nodetree = ntreeFromID(id); if (nodetree != NULL) { - nodetree->id.recalc_undo_accumulated = 0; + nodetree->id.recalc_after_undo_push = 0; } if (GS(id->name) == ID_SCE) { Scene *scene = (Scene *)id; if (scene->master_collection != NULL) { - scene->master_collection->id.recalc_undo_accumulated = 0; + scene->master_collection->id.recalc_after_undo_push = 0; } } } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 1e894d44f87..dd3964dfc15 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -246,11 +246,16 @@ typedef struct ID { int icon_id; int recalc; /** - * Used by undo code. Value of recalc is stored there when reading an ID from memfile, and not - * touched by anything, which means it can be used as 'reference' recalc value for the next undo - * step, when going backward (i.e. actual undo, redo can just use recalc value directly). + * Used by undo code. recalc_after_undo_push contains the changes between the + * last undo push and the current state. This is accumulated as IDs are tagged + * for update in the depsgraph, and only cleared on undo push. + * + * recalc_up_to_undo_push is saved to undo memory, and is the value of + * recalc_after_undo_push at the time of the undo push. This means it can be + * used to find the changes between undo states. */ - int recalc_undo_accumulated; + int recalc_up_to_undo_push; + int recalc_after_undo_push; /** * A session-wide unique identifier for a given ID, that remain the same across potential @@ -258,8 +263,6 @@ typedef struct ID { */ unsigned int session_uuid; - char _pad[4]; - IDProperty *properties; /** Reference linked ID which this one overrides. */ -- cgit v1.2.3 From 6e272b9ba429dc7f4fc7724f19254b1021a9559b Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 14 Apr 2020 11:29:10 +0200 Subject: BLI_math: add min/max utils for chars. --- source/blender/blenlib/BLI_math_base.h | 3 +++ source/blender/blenlib/intern/math_base_inline.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index 7a9d17d2b05..ae9617d2f16 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -151,6 +151,9 @@ MINLINE int max_iiii(int a, int b, int c, int d); MINLINE size_t min_zz(size_t a, size_t b); MINLINE size_t max_zz(size_t a, size_t b); +MINLINE char min_cc(char a, char b); +MINLINE char max_cc(char a, char b); + MINLINE int clamp_i(int value, int min, int max); MINLINE float clamp_f(float value, float min, float max); MINLINE size_t clamp_z(size_t value, size_t min, size_t max); diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index c4b68e9164c..e0cac508d28 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -524,6 +524,15 @@ MINLINE size_t max_zz(size_t a, size_t b) return (b < a) ? a : b; } +MINLINE char min_cc(char a, char b) +{ + return (a < b) ? a : b; +} +MINLINE char max_cc(char a, char b) +{ + return (b < a) ? a : b; +} + MINLINE int clamp_i(int value, int min, int max) { return min_ii(max_ii(value, min), max); -- cgit v1.2.3 From b07e8a24f5c69578c5ccae31848bb0f51fd18700 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 14 Apr 2020 11:50:36 +0200 Subject: Cleanup: remove unnecessary branch when lib linking constraints Differential Revision: https://developer.blender.org/D7386 Reviewers: mont29 --- source/blender/blenloader/intern/readfile.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 168b4e01e8d..9ff5ab3a648 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -3834,20 +3834,11 @@ typedef struct tConstraintLinkData { /* callback function used to relink constraint ID-links */ static void lib_link_constraint_cb(bConstraint *UNUSED(con), ID **idpoin, - bool is_reference, + bool UNUSED(is_reference), void *userdata) { tConstraintLinkData *cld = (tConstraintLinkData *)userdata; - - /* for reference types, we need to increment the user-counts on load... */ - if (is_reference) { - /* reference type - with usercount */ - *idpoin = newlibadr(cld->fd, cld->id->lib, *idpoin); - } - else { - /* target type - no usercount needed */ - *idpoin = newlibadr(cld->fd, cld->id->lib, *idpoin); - } + *idpoin = newlibadr(cld->fd, cld->id->lib, *idpoin); } static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist) -- cgit v1.2.3