From 394c5318c6fe981f94fc78f312fc0295209288dc Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Fri, 17 Apr 2015 12:25:37 -0300 Subject: Bake-API: reduce memory footprint when baking more than one object (Fix T41092) Combine all the highpoly pixel arrays into a single array with a lookup object_id for each of the highpoly objects. Note: This changes the Bake API, external engines should refer to the bake_api.c for the latest API. Many thanks for Sergey Sharybin for the complete review, changes suggestion and feedback. (you rock!) Reviewers: sergey Subscribers: pildanovak, marcclintdion, monio, metalliandy, brecht Maniphest Tasks: T41092 Differential Revision: https://developer.blender.org/D772 --- source/blender/editors/object/object_bake_api.c | 18 ++++---- source/blender/makesrna/intern/rna_render.c | 13 +++++- source/blender/render/extern/include/RE_bake.h | 7 ++- source/blender/render/extern/include/RE_engine.h | 2 +- source/blender/render/intern/source/bake_api.c | 54 +++++++++++----------- .../blender/render/intern/source/external_engine.c | 5 +- 6 files changed, 55 insertions(+), 44 deletions(-) (limited to 'source') diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index d492a4b9dda..578335a385b 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -574,6 +574,7 @@ static int bake( float *result = NULL; BakePixel *pixel_array_low = NULL; + BakePixel *pixel_array_high = NULL; const bool is_save_internal = (save_mode == R_BAKE_SAVE_INTERNAL); const bool is_noncolor = is_noncolor_pass(pass_type); @@ -682,6 +683,7 @@ static int bake( } pixel_array_low = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly"); + pixel_array_high = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly"); result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels"); /* get the mesh as it arrives in the renderer */ @@ -755,8 +757,6 @@ static int bake( /* initialize highpoly_data */ highpoly[i].ob = ob_iter; highpoly[i].restrict_flag = ob_iter->restrictflag; - highpoly[i].pixel_array = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly"); - /* triangulating so BVH returns the primitive_id that will be used for rendering */ highpoly[i].tri_mod = ED_object_modifier_add( @@ -790,7 +790,7 @@ static int bake( /* populate the pixel arrays with the corresponding face data for each high poly object */ if (!RE_bake_pixels_populate_from_objects( - me_low, pixel_array_low, highpoly, tot_highpoly, num_pixels, ob_cage != NULL, + me_low, pixel_array_low, pixel_array_high, highpoly, tot_highpoly, num_pixels, ob_cage != NULL, cage_extrusion, ob_low->obmat, (ob_cage ? ob_cage->obmat : ob_low->obmat), me_cage)) { BKE_report(reports, RPT_ERROR, "Error handling selected objects"); @@ -799,8 +799,8 @@ static int bake( /* the baking itself */ for (i = 0; i < tot_highpoly; i++) { - ok = RE_bake_engine(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels, - depth, pass_type, result); + ok = RE_bake_engine(re, highpoly[i].ob, i, pixel_array_high, + num_pixels, depth, pass_type, result); if (!ok) { BKE_reportf(reports, RPT_ERROR, "Error baking from object \"%s\"", highpoly[i].ob->id.name + 2); goto cage_cleanup; @@ -826,7 +826,7 @@ cage_cleanup: ob_low->restrictflag &= ~OB_RESTRICT_RENDER; if (RE_bake_has_engine(re)) { - ok = RE_bake_engine(re, ob_low, pixel_array_low, num_pixels, depth, pass_type, result); + ok = RE_bake_engine(re, ob_low, 0, pixel_array_low, num_pixels, depth, pass_type, result); } else { BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); @@ -990,9 +990,6 @@ cleanup: for (i = 0; i < tot_highpoly; i++) { highpoly[i].ob->restrictflag = highpoly[i].restrict_flag; - if (highpoly[i].pixel_array) - MEM_freeN(highpoly[i].pixel_array); - if (highpoly[i].tri_mod) ED_object_modifier_remove(reports, bmain, highpoly[i].ob, highpoly[i].tri_mod); @@ -1010,6 +1007,9 @@ cleanup: if (pixel_array_low) MEM_freeN(pixel_array_low); + if (pixel_array_high) + MEM_freeN(pixel_array_high); + if (bake_images.data) MEM_freeN(bake_images.data); diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 2f2d72577d5..dbb572b7333 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -154,8 +154,10 @@ static void engine_render(RenderEngine *engine, struct Scene *scene) RNA_parameter_list_free(&list); } -static void engine_bake(RenderEngine *engine, struct Scene *scene, struct Object *object, const int pass_type, - const struct BakePixel *pixel_array, const int num_pixels, const int depth, void *result) +static void engine_bake(RenderEngine *engine, struct Scene *scene, + struct Object *object, const int pass_type, + const int object_id, const struct BakePixel *pixel_array, + const int num_pixels, const int depth, void *result) { extern FunctionRNA rna_RenderEngine_bake_func; PointerRNA ptr; @@ -169,6 +171,7 @@ static void engine_bake(RenderEngine *engine, struct Scene *scene, struct Object RNA_parameter_set_lookup(&list, "scene", &scene); RNA_parameter_set_lookup(&list, "object", &object); RNA_parameter_set_lookup(&list, "pass_type", &pass_type); + RNA_parameter_set_lookup(&list, "object_id", &object_id); RNA_parameter_set_lookup(&list, "pixel_array", &pixel_array); RNA_parameter_set_lookup(&list, "num_pixels", &num_pixels); RNA_parameter_set_lookup(&list, "depth", &depth); @@ -423,6 +426,8 @@ static void rna_def_render_engine(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REQUIRED); prop = RNA_def_enum(func, "pass_type", render_pass_type_items, 0, "Pass", "Pass to bake"); RNA_def_property_flag(prop, PROP_REQUIRED); + prop = RNA_def_int(func, "object_id", 0, 0, INT_MAX, "Object Id", "Id of the current object being baked in relation to the others", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_REQUIRED); prop = RNA_def_pointer(func, "pixel_array", "BakePixel", "", ""); RNA_def_property_flag(prop, PROP_REQUIRED); prop = RNA_def_int(func, "num_pixels", 0, 0, INT_MAX, "Number of Pixels", "Size of the baking batch", 0, INT_MAX); @@ -816,6 +821,10 @@ static void rna_def_render_bake_pixel(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "primitive_id"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "object_id", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "object_id"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "uv", PROP_FLOAT, PROP_NONE); RNA_def_property_array(prop, 2); RNA_def_property_float_sdna(prop, NULL, "uv"); diff --git a/source/blender/render/extern/include/RE_bake.h b/source/blender/render/extern/include/RE_bake.h index ffc1246b878..e8a8986714d 100644 --- a/source/blender/render/extern/include/RE_bake.h +++ b/source/blender/render/extern/include/RE_bake.h @@ -50,14 +50,13 @@ typedef struct BakeImages { } BakeImages; typedef struct BakePixel { - int primitive_id; + int primitive_id, object_id; float uv[2]; float du_dx, du_dy; float dv_dx, dv_dy; } BakePixel; typedef struct BakeHighPolyData { - struct BakePixel *pixel_array; struct Object *ob; struct ModifierData *tri_mod; struct Mesh *me; @@ -72,7 +71,7 @@ typedef struct BakeHighPolyData { bool RE_bake_has_engine(struct Render *re); bool RE_bake_engine( - struct Render *re, struct Object *object, const BakePixel pixel_array[], + struct Render *re, struct Object *object, const int object_id, const BakePixel pixel_array[], const size_t num_pixels, const int depth, const ScenePassType pass_type, float result[]); /* bake.c */ @@ -82,7 +81,7 @@ bool RE_bake_internal( const size_t num_pixels, const int depth, const ScenePassType pass_type, float result[]); bool RE_bake_pixels_populate_from_objects( - struct Mesh *me_low, BakePixel pixel_array_from[], + struct Mesh *me_low, BakePixel pixel_array_from[], BakePixel pixel_array_to[], BakeHighPolyData highpoly[], const int tot_highpoly, const size_t num_pixels, const bool is_custom_cage, const float cage_extrusion, float mat_low[4][4], float mat_cage[4][4], struct Mesh *me_cage); diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h index 4857c409f08..9d3c74163d5 100644 --- a/source/blender/render/extern/include/RE_engine.h +++ b/source/blender/render/extern/include/RE_engine.h @@ -88,7 +88,7 @@ typedef struct RenderEngineType { void (*update)(struct RenderEngine *engine, struct Main *bmain, struct Scene *scene); void (*render)(struct RenderEngine *engine, struct Scene *scene); - void (*bake)(struct RenderEngine *engine, struct Scene *scene, struct Object *object, const int pass_type, const struct BakePixel *pixel_array, const int num_pixels, const int depth, void *result); + void (*bake)(struct RenderEngine *engine, struct Scene *scene, struct Object *object, const int pass_type, const int object_id, const struct BakePixel *pixel_array, const int num_pixels, const int depth, void *result); void (*view_update)(struct RenderEngine *engine, const struct bContext *context); void (*view_draw)(struct RenderEngine *engine, const struct bContext *context); diff --git a/source/blender/render/intern/source/bake_api.c b/source/blender/render/intern/source/bake_api.c index ecfb86c8d17..1f7555bc5c5 100644 --- a/source/blender/render/intern/source/bake_api.c +++ b/source/blender/render/intern/source/bake_api.c @@ -30,10 +30,11 @@ * * The Bake API is fully implemented with Python rna functions. The operator expects/call a function: * - * ``def bake(scene, object, pass_type, pixel_array, num_pixels, depth, result)`` + * ``def bake(scene, object, pass_type, object_id, pixel_array, num_pixels, depth, result)`` * - scene: current scene (Python object) * - object: object to render (Python object) * - pass_type: pass to render (string, e.g., "COMBINED", "AO", "NORMAL", ...) + * - object_id: index of object to bake (to use with the pixel_array) * - pixel_array: list of primitive ids and barycentric coordinates to bake(Python object, see bake_pixel) * - num_pixels: size of pixel_array, number of pixels to bake (int) * - depth: depth of pixels to return (int, assuming always 4 now) @@ -47,7 +48,7 @@ * * \code{.c} * struct BakePixel { - * int primitive_id; + * int primitive_id, object_id; * float uv[2]; * float du_dx, du_dy; * float dv_dx, dv_dy; @@ -55,7 +56,7 @@ * \endcode * * In python you have access to: - * - ``primitive_id``, ``uv``, ``du_dx``, ``du_dy``, ``next`` + * - ``primitive_id``, ``object_id``, ``uv``, ``du_dx``, ``du_dy``, ``next`` * - ``next()`` is a function that returns the next #BakePixel in the array. * * \note Pixels that should not be baked have ``primitive_id == -1`` @@ -126,12 +127,16 @@ static void store_bake_pixel(void *handle, int x, int y, float u, float v) pixel = &bd->pixel_array[i]; pixel->primitive_id = bd->primitive_id; + /* At this point object_id is always 0, since this function runs for the + * lowpoly mesh only. The object_id lookup indices are set afterwards. */ + copy_v2_fl2(pixel->uv, u, v); pixel->du_dx = bd->du_dx; pixel->du_dy = bd->du_dy; pixel->dv_dx = bd->dv_dx; pixel->dv_dy = bd->dv_dy; + pixel->object_id = 0; } void RE_bake_mask_fill(const BakePixel pixel_array[], const size_t num_pixels, char *mask) @@ -271,7 +276,7 @@ static void calc_barycentric_from_point( * This function populates pixel_array and returns TRUE if things are correct */ static bool cast_ray_highpoly( - BVHTreeFromMesh *treeData, TriTessFace *triangles[], BakeHighPolyData *highpoly, + BVHTreeFromMesh *treeData, TriTessFace *triangles[], BakePixel *pixel_array, BakeHighPolyData *highpoly, const float co[3], const float dir[3], const int pixel_id, const int tot_highpoly, const float du_dx, const float du_dy, const float dv_dx, const float dv_dy) { @@ -322,22 +327,22 @@ static bool cast_ray_highpoly( } } - for (i = 0; i < tot_highpoly; i++) { - if (hit_mesh == i) { - calc_barycentric_from_point(triangles[i], hits[i].index, hits[i].co, &primitive_id, uv); - highpoly[i].pixel_array[pixel_id].primitive_id = primitive_id; - copy_v2_v2(highpoly[i].pixel_array[pixel_id].uv, uv); - - /* the differentials are relative to the UV/image space, so the highpoly differentials - * are the same as the low poly differentials */ - highpoly[i].pixel_array[pixel_id].du_dx = du_dx; - highpoly[i].pixel_array[pixel_id].du_dy = du_dy; - highpoly[i].pixel_array[pixel_id].dv_dx = dv_dx; - highpoly[i].pixel_array[pixel_id].dv_dy = dv_dy; - } - else { - highpoly[i].pixel_array[pixel_id].primitive_id = -1; - } + if (hit_mesh != -1) { + calc_barycentric_from_point(triangles[hit_mesh], hits[hit_mesh].index, hits[hit_mesh].co, &primitive_id, uv); + pixel_array[pixel_id].primitive_id = primitive_id; + pixel_array[pixel_id].object_id = hit_mesh; + copy_v2_v2(pixel_array[pixel_id].uv, uv); + + /* the differentials are relative to the UV/image space, so the highpoly differentials + * are the same as the low poly differentials */ + pixel_array[pixel_id].du_dx = du_dx; + pixel_array[pixel_id].du_dy = du_dy; + pixel_array[pixel_id].dv_dx = dv_dx; + pixel_array[pixel_id].dv_dy = dv_dy; + } + else { + pixel_array[pixel_id].primitive_id = -1; + pixel_array[pixel_id].object_id = -1; } MEM_freeN(hits); @@ -437,7 +442,7 @@ static void mesh_calc_tri_tessface( } bool RE_bake_pixels_populate_from_objects( - struct Mesh *me_low, BakePixel pixel_array_from[], + struct Mesh *me_low, BakePixel pixel_array_from[], BakePixel pixel_array_to[], BakeHighPolyData highpoly[], const int tot_highpoly, const size_t num_pixels, const bool is_custom_cage, const float cage_extrusion, float mat_low[4][4], float mat_cage[4][4], struct Mesh *me_cage) { @@ -508,10 +513,7 @@ bool RE_bake_pixels_populate_from_objects( primitive_id = pixel_array_from[i].primitive_id; if (primitive_id == -1) { - int j; - for (j = 0; j < tot_highpoly; j++) { - highpoly[j].pixel_array[i].primitive_id = -1; - } + pixel_array_to[i].primitive_id = -1; continue; } @@ -530,7 +532,7 @@ bool RE_bake_pixels_populate_from_objects( } /* cast ray */ - if (!cast_ray_highpoly(treeData, tris_high, highpoly, co, dir, i, tot_highpoly, + if (!cast_ray_highpoly(treeData, tris_high, pixel_array_to, highpoly, co, dir, i, tot_highpoly, pixel_array_from[i].du_dx, pixel_array_from[i].du_dy, pixel_array_from[i].dv_dx, pixel_array_from[i].dv_dy)) { /* if it fails mask out the original pixel array */ diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index 343c4a9387a..653af7460f9 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -463,7 +463,8 @@ bool RE_bake_has_engine(Render *re) } bool RE_bake_engine( - Render *re, Object *object, const BakePixel pixel_array[], + Render *re, Object *object, + const int object_id, const BakePixel pixel_array[], const size_t num_pixels, const int depth, const ScenePassType pass_type, float result[]) { @@ -501,7 +502,7 @@ bool RE_bake_engine( type->update(engine, re->main, re->scene); if (type->bake) - type->bake(engine, re->scene, object, pass_type, pixel_array, num_pixels, depth, result); + type->bake(engine, re->scene, object, pass_type, object_id, pixel_array, num_pixels, depth, result); engine->tile_x = 0; engine->tile_y = 0; -- cgit v1.2.3