Correct over-alloc struct declaration
[blender.git] / source / blender / render / intern / source / external_engine.c
1 /*
2
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version. 
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * The Original Code is Copyright (C) 2006 Blender Foundation.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 /** \file blender/render/intern/source/external_engine.c
30  *  \ingroup render
31  */
32
33 #include <stddef.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "BLT_translation.h"
40
41 #include "BLI_listbase.h"
42 #include "BLI_string.h"
43 #include "BLI_utildefines.h"
44
45 #include "BKE_camera.h"
46 #include "BKE_global.h"
47 #include "BKE_colortools.h"
48 #include "BKE_layer.h"
49 #include "BKE_report.h"
50 #include "BKE_scene.h"
51
52 #include "DEG_depsgraph.h"
53
54 #include "RNA_access.h"
55
56 #ifdef WITH_PYTHON
57 #include "BPY_extern.h"
58 #endif
59
60 #include "RE_engine.h"
61 #include "RE_pipeline.h"
62 #include "RE_bake.h"
63
64 #include "DRW_engine.h"
65
66 #include "initrender.h"
67 #include "renderpipeline.h"
68 #include "render_types.h"
69 #include "render_result.h"
70
71 /* Render Engine Types */
72
73 static RenderEngineType internal_render_type = {
74         NULL, NULL,
75         "BLENDER_RENDER", N_("Blender Render"), RE_INTERNAL | RE_USE_LEGACY_PIPELINE,
76         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
77         {NULL, NULL, NULL}
78 };
79
80 #ifdef WITH_GAMEENGINE
81
82 static RenderEngineType internal_game_type = {
83         NULL, NULL,
84         "BLENDER_GAME", N_("Blender Game"), RE_INTERNAL | RE_GAME | RE_USE_LEGACY_PIPELINE,
85         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
86         {NULL, NULL, NULL}
87 };
88
89 #endif
90
91 ListBase R_engines = {NULL, NULL};
92
93 void RE_engines_init(void)
94 {
95         RE_engines_register(NULL, &internal_render_type);
96 #ifdef WITH_GAMEENGINE
97         RE_engines_register(NULL, &internal_game_type);
98 #endif
99         DRW_engines_register();
100 }
101
102 void RE_engines_exit(void)
103 {
104         RenderEngineType *type, *next;
105
106         DRW_engines_free();
107
108         BKE_layer_collection_engine_settings_callback_free();
109
110         for (type = R_engines.first; type; type = next) {
111                 next = type->next;
112
113                 BLI_remlink(&R_engines, type);
114
115                 if (!(type->flag & RE_INTERNAL)) {
116                         if (type->ext.free)
117                                 type->ext.free(type->ext.data);
118
119                         MEM_freeN(type);
120                 }
121         }
122 }
123
124 void RE_engines_register(Main *bmain, RenderEngineType *render_type)
125 {
126         if (render_type->draw_engine) {
127                 DRW_engine_register(render_type->draw_engine);
128         }
129         if (render_type->collection_settings_create) {
130                 BKE_layer_collection_engine_settings_callback_register(
131                             bmain, render_type->idname, render_type->collection_settings_create);
132         }
133         BLI_addtail(&R_engines, render_type);
134 }
135
136 RenderEngineType *RE_engines_find(const char *idname)
137 {
138         RenderEngineType *type;
139         
140         type = BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname));
141         if (!type)
142                 type = &internal_render_type;
143         
144         return type;
145 }
146
147 bool RE_engine_is_external(Render *re)
148 {
149         RenderEngineType *type = RE_engines_find(re->r.engine);
150         return (type && type->render_to_image);
151 }
152
153 /* Create, Free */
154
155 RenderEngine *RE_engine_create(RenderEngineType *type)
156 {
157         return RE_engine_create_ex(type, false);
158 }
159
160 RenderEngine *RE_engine_create_ex(RenderEngineType *type, bool use_for_viewport)
161 {
162         RenderEngine *engine = MEM_callocN(sizeof(RenderEngine), "RenderEngine");
163         engine->type = type;
164
165         if (use_for_viewport) {
166                 engine->flag |= RE_ENGINE_USED_FOR_VIEWPORT;
167
168                 BLI_begin_threaded_malloc();
169         }
170
171         return engine;
172 }
173
174 void RE_engine_free(RenderEngine *engine)
175 {
176 #ifdef WITH_PYTHON
177         if (engine->py_instance) {
178                 BPY_DECREF_RNA_INVALIDATE(engine->py_instance);
179         }
180 #endif
181
182         if (engine->flag & RE_ENGINE_USED_FOR_VIEWPORT) {
183                 BLI_end_threaded_malloc();
184         }
185
186         MEM_freeN(engine);
187 }
188
189 /* Render Results */
190
191 static RenderPart *get_part_from_result(Render *re, RenderResult *result)
192 {
193         RenderPart *pa;
194
195         for (pa = re->parts.first; pa; pa = pa->next) {
196                 if (result->tilerect.xmin == pa->disprect.xmin - re->disprect.xmin &&
197                     result->tilerect.ymin == pa->disprect.ymin - re->disprect.ymin &&
198                     result->tilerect.xmax == pa->disprect.xmax - re->disprect.xmin &&
199                     result->tilerect.ymax == pa->disprect.ymax - re->disprect.ymin)
200                 {
201                         return pa;
202                 }
203         }
204
205         return NULL;
206 }
207
208 RenderResult *RE_engine_begin_result(
209         RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname)
210 {
211         Render *re = engine->re;
212         RenderResult *result;
213         rcti disprect;
214
215         /* ensure the coordinates are within the right limits */
216         CLAMP(x, 0, re->result->rectx);
217         CLAMP(y, 0, re->result->recty);
218         CLAMP(w, 0, re->result->rectx);
219         CLAMP(h, 0, re->result->recty);
220
221         if (x + w > re->result->rectx)
222                 w = re->result->rectx - x;
223         if (y + h > re->result->recty)
224                 h = re->result->recty - y;
225
226         /* allocate a render result */
227         disprect.xmin = x;
228         disprect.xmax = x + w;
229         disprect.ymin = y;
230         disprect.ymax = y + h;
231
232         result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname);
233
234         /* todo: make this thread safe */
235
236         /* can be NULL if we CLAMP the width or height to 0 */
237         if (result) {
238                 RenderPart *pa;
239
240                 /* Copy EXR tile settings, so pipeline knows whether this is a result
241                  * for Save Buffers enabled rendering.
242                  */
243                 result->do_exr_tile = re->result->do_exr_tile;
244
245                 BLI_addtail(&engine->fullresult, result);
246
247                 result->tilerect.xmin += re->disprect.xmin;
248                 result->tilerect.xmax += re->disprect.xmin;
249                 result->tilerect.ymin += re->disprect.ymin;
250                 result->tilerect.ymax += re->disprect.ymin;
251
252                 pa = get_part_from_result(re, result);
253
254                 if (pa)
255                         pa->status = PART_STATUS_IN_PROGRESS;
256         }
257
258         return result;
259 }
260
261 void RE_engine_update_result(RenderEngine *engine, RenderResult *result)
262 {
263         Render *re = engine->re;
264
265         if (result) {
266                 result->renlay = result->layers.first; /* weak, draws first layer always */
267                 re->display_update(re->duh, result, NULL);
268         }
269 }
270
271 void RE_engine_end_result(RenderEngine *engine, RenderResult *result, int cancel, int merge_results)
272 {
273         Render *re = engine->re;
274
275         if (!result) {
276                 return;
277         }
278
279         /* merge. on break, don't merge in result for preview renders, looks nicer */
280         if (!cancel) {
281                 /* for exr tile render, detect tiles that are done */
282                 RenderPart *pa = get_part_from_result(re, result);
283
284                 if (pa) {
285                         pa->status = PART_STATUS_READY;
286                 }
287                 else if (re->result->do_exr_tile) {
288                         /* if written result does not match any tile and we are using save
289                          * buffers, we are going to get openexr save errors */
290                         fprintf(stderr, "RenderEngine.end_result: dimensions do not match any OpenEXR tile.\n");
291                 }
292         }
293
294         if (!cancel || merge_results) {
295                 if (re->result->do_exr_tile) {
296                         if (!cancel) {
297                                 render_result_exr_file_merge(re->result, result, re->viewname);
298                         }
299                 }
300                 else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW)))
301                         render_result_merge(re->result, result);
302
303                 /* draw */
304                 if (!re->test_break(re->tbh)) {
305                         result->renlay = result->layers.first; /* weak, draws first layer always */
306                         re->display_update(re->duh, result, NULL);
307                 }
308         }
309
310         /* free */
311         BLI_remlink(&engine->fullresult, result);
312         render_result_free(result);
313 }
314
315 /* Cancel */
316
317 int RE_engine_test_break(RenderEngine *engine)
318 {
319         Render *re = engine->re;
320
321         if (re)
322                 return re->test_break(re->tbh);
323         
324         return 0;
325 }
326
327 /* Statistics */
328
329 void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info)
330 {
331         Render *re = engine->re;
332
333         /* stats draw callback */
334         if (re) {
335                 re->i.statstr = stats;
336                 re->i.infostr = info;
337                 re->stats_draw(re->sdh, &re->i);
338                 re->i.infostr = NULL;
339                 re->i.statstr = NULL;
340         }
341
342         /* set engine text */
343         engine->text[0] = '\0';
344
345         if (stats && stats[0] && info && info[0])
346                 BLI_snprintf(engine->text, sizeof(engine->text), "%s | %s", stats, info);
347         else if (info && info[0])
348                 BLI_strncpy(engine->text, info, sizeof(engine->text));
349         else if (stats && stats[0])
350                 BLI_strncpy(engine->text, stats, sizeof(engine->text));
351 }
352
353 void RE_engine_update_progress(RenderEngine *engine, float progress)
354 {
355         Render *re = engine->re;
356
357         if (re) {
358                 CLAMP(progress, 0.0f, 1.0f);
359                 re->progress(re->prh, progress);
360         }
361 }
362
363 void RE_engine_update_memory_stats(RenderEngine *engine, float mem_used, float mem_peak)
364 {
365         Render *re = engine->re;
366
367         if (re) {
368                 re->i.mem_used = mem_used;
369                 re->i.mem_peak = mem_peak;
370         }
371 }
372
373 void RE_engine_report(RenderEngine *engine, int type, const char *msg)
374 {
375         Render *re = engine->re;
376
377         if (re)
378                 BKE_report(engine->re->reports, type, msg);
379         else if (engine->reports)
380                 BKE_report(engine->reports, type, msg);
381 }
382
383 void RE_engine_set_error_message(RenderEngine *engine, const char *msg)
384 {
385         Render *re = engine->re;
386         if (re != NULL) {
387                 RenderResult *rr = RE_AcquireResultRead(re);
388                 if (rr) {
389                         if (rr->error != NULL) {
390                                 MEM_freeN(rr->error);
391                         }
392                         rr->error = BLI_strdup(msg);
393                 }
394                 RE_ReleaseResult(re);
395         }
396 }
397
398 const char *RE_engine_active_view_get(RenderEngine *engine)
399 {
400         Render *re = engine->re;
401         return RE_GetActiveRenderView(re);
402 }
403
404 void RE_engine_active_view_set(RenderEngine *engine, const char *viewname)
405 {
406         Render *re = engine->re;
407         RE_SetActiveRenderView(re, viewname);
408 }
409
410 float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, int use_spherical_stereo)
411 {
412         Render *re = engine->re;
413
414         /* when using spherical stereo, get camera shift without multiview, leaving stereo to be handled by the engine */
415         if (use_spherical_stereo)
416                 re = NULL;
417
418         return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname);
419 }
420
421 void RE_engine_get_camera_model_matrix(
422         RenderEngine *engine, Object *camera, int use_spherical_stereo, float *r_modelmat)
423 {
424         Render *re = engine->re;
425
426         /* when using spherical stereo, get model matrix without multiview, leaving stereo to be handled by the engine */
427         if (use_spherical_stereo)
428                 re = NULL;
429
430         BKE_camera_multiview_model_matrix(re ? &re->r : NULL, camera, re->viewname, (float (*)[4])r_modelmat);
431 }
432
433 int RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera)
434 {
435         Render *re = engine->re;
436         return BKE_camera_multiview_spherical_stereo(re ? &re->r : NULL, camera) ? 1 : 0;
437 }
438
439 rcti* RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free)
440 {
441         static rcti tiles_static[BLENDER_MAX_THREADS];
442         const int allocation_step = BLENDER_MAX_THREADS;
443         RenderPart *pa;
444         int total_tiles = 0;
445         rcti *tiles = tiles_static;
446         int allocation_size = BLENDER_MAX_THREADS;
447
448         BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_READ);
449
450         *r_needs_free = false;
451
452         if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) {
453                 *r_total_tiles = 0;
454                 BLI_rw_mutex_unlock(&re->partsmutex);
455                 return NULL;
456         }
457
458         for (pa = re->parts.first; pa; pa = pa->next) {
459                 if (pa->status == PART_STATUS_IN_PROGRESS) {
460                         if (total_tiles >= allocation_size) {
461                                 /* Just in case we're using crazy network rendering with more
462                                  * slaves as BLENDER_MAX_THREADS.
463                                  */
464                                 allocation_size += allocation_step;
465                                 if (tiles == tiles_static) {
466                                         /* Can not realloc yet, tiles are pointing to a
467                                          * stack memory.
468                                          */
469                                         tiles = MEM_mallocN(allocation_size * sizeof(rcti), "current engine tiles");
470                                 }
471                                 else {
472                                         tiles = MEM_reallocN(tiles, allocation_size * sizeof(rcti));
473                                 }
474                                 *r_needs_free = true;
475                         }
476                         tiles[total_tiles] = pa->disprect;
477
478                         if (pa->crop) {
479                                 tiles[total_tiles].xmin += pa->crop;
480                                 tiles[total_tiles].ymin += pa->crop;
481                                 tiles[total_tiles].xmax -= pa->crop;
482                                 tiles[total_tiles].ymax -= pa->crop;
483                         }
484
485                         total_tiles++;
486                 }
487         }
488         BLI_rw_mutex_unlock(&re->partsmutex);
489         *r_total_tiles = total_tiles;
490         return tiles;
491 }
492
493 RenderData *RE_engine_get_render_data(Render *re)
494 {
495         return &re->r;
496 }
497
498 /* Bake */
499 void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Depsgraph *graph, Scene *scene)
500 {
501         re->depsgraph = graph;
502         re->scene = scene;
503         re->main = bmain;
504         render_copy_renderdata(&re->r, &scene->r);
505 }
506
507 bool RE_bake_has_engine(Render *re)
508 {
509         RenderEngineType *type = RE_engines_find(re->r.engine);
510         return (type->bake != NULL);
511 }
512
513 bool RE_bake_engine(
514         Render *re, Object *object,
515         const int object_id, const BakePixel pixel_array[],
516         const size_t num_pixels, const int depth,
517         const ScenePassType pass_type, const int pass_filter,
518         float result[])
519 {
520         RenderEngineType *type = RE_engines_find(re->r.engine);
521         RenderEngine *engine;
522         bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0;
523
524         /* set render info */
525         re->i.cfra = re->scene->r.cfra;
526         BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name) - 2);
527         re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0;
528
529         /* render */
530         engine = re->engine;
531
532         if (!engine) {
533                 engine = RE_engine_create(type);
534                 re->engine = engine;
535         }
536
537         engine->flag |= RE_ENGINE_RENDERING;
538
539         /* TODO: actually link to a parent which shouldn't happen */
540         engine->re = re;
541
542         engine->resolution_x = re->winx;
543         engine->resolution_y = re->winy;
544
545         RE_parts_init(re, false);
546         engine->tile_x = re->r.tilex;
547         engine->tile_y = re->r.tiley;
548
549         /* update is only called so we create the engine.session */
550         if (type->update)
551                 type->update(engine, re->main, re->depsgraph, re->scene);
552
553         if (type->bake) {
554                 type->bake(
555                             engine,
556                             re->scene,
557                             object,
558                             pass_type,
559                             pass_filter,
560                             object_id,
561                             pixel_array,
562                             num_pixels,
563                             depth,
564                             result);
565         }
566
567         engine->tile_x = 0;
568         engine->tile_y = 0;
569         engine->flag &= ~RE_ENGINE_RENDERING;
570
571         BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE);
572
573         /* re->engine becomes zero if user changed active render engine during render */
574         if (!persistent_data || !re->engine) {
575                 RE_engine_free(engine);
576                 re->engine = NULL;
577         }
578
579         RE_parts_free(re);
580         BLI_rw_mutex_unlock(&re->partsmutex);
581
582         if (BKE_reports_contain(re->reports, RPT_ERROR))
583                 G.is_break = true;
584
585         return true;
586 }
587
588 void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
589 {
590         Render *re = engine->re;
591         Scene *scene = re->scene;
592         double cfra = (double)frame + (double)subframe;
593
594         CLAMP(cfra, MINAFRAME, MAXFRAME);
595         BKE_scene_frame_set(scene, cfra);
596
597 #ifdef WITH_PYTHON
598         BPy_BEGIN_ALLOW_THREADS;
599 #endif
600
601         BKE_scene_update_for_newframe(re->eval_ctx, re->main, scene);
602
603 #ifdef WITH_PYTHON
604         BPy_END_ALLOW_THREADS;
605 #endif
606
607         BKE_scene_camera_switch_update(scene);
608 }
609
610 /* Render */
611
612 int RE_engine_render(Render *re, int do_all)
613 {
614         RenderEngineType *type = RE_engines_find(re->r.engine);
615         RenderEngine *engine;
616         bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0;
617
618         /* verify if we can render */
619         if (!type->render_to_image)
620                 return 0;
621         if ((re->r.scemode & R_BUTS_PREVIEW) && !(type->flag & RE_USE_PREVIEW))
622                 return 0;
623         if (do_all && !(type->flag & RE_USE_POSTPROCESS))
624                 return 0;
625         if (!do_all && (type->flag & RE_USE_POSTPROCESS))
626                 return 0;
627
628         /* Lock drawing in UI during data phase. */
629         if (re->draw_lock) {
630                 re->draw_lock(re->dlh, 1);
631         }
632
633         /* update animation here so any render layer animation is applied before
634          * creating the render result */
635         if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) {
636                 BKE_scene_update_for_newframe(re->eval_ctx, re->main, re->scene);
637                 render_update_anim_renderdata(re, &re->scene->r);
638         }
639
640         /* create render result */
641         BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
642         if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) {
643                 int savebuffers = RR_USE_MEM;
644
645                 if (re->result)
646                         render_result_free(re->result);
647
648                 if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE))
649                         savebuffers = RR_USE_EXR;
650                 re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS);
651         }
652         BLI_rw_mutex_unlock(&re->resultmutex);
653
654         if (re->result == NULL) {
655                 /* Clear UI drawing locks. */
656                 if (re->draw_lock) {
657                         re->draw_lock(re->dlh, 0);
658                 }
659                 /* Too small image is handled earlier, here it could only happen if
660                  * there was no sufficient memory to allocate all passes.
661                  */
662                 BKE_report(re->reports, RPT_ERROR, "Failed allocate render result, out of memory");
663                 G.is_break = true;
664                 return 1;
665         }
666
667         /* set render info */
668         re->i.cfra = re->scene->r.cfra;
669         BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name));
670         re->i.totface = re->i.totvert = re->i.totstrand = re->i.totlamp = re->i.tothalo = 0;
671
672         /* render */
673         engine = re->engine;
674
675         if (!engine) {
676                 engine = RE_engine_create(type);
677                 re->engine = engine;
678         }
679
680         engine->flag |= RE_ENGINE_RENDERING;
681
682         /* TODO: actually link to a parent which shouldn't happen */
683         engine->re = re;
684
685         if (re->flag & R_ANIMATION)
686                 engine->flag |= RE_ENGINE_ANIMATION;
687         if (re->r.scemode & R_BUTS_PREVIEW)
688                 engine->flag |= RE_ENGINE_PREVIEW;
689         engine->camera_override = re->camera_override;
690         engine->layer_override = re->layer_override;
691
692         engine->resolution_x = re->winx;
693         engine->resolution_y = re->winy;
694
695         RE_parts_init(re, false);
696         engine->tile_x = re->partx;
697         engine->tile_y = re->party;
698
699         if (re->result->do_exr_tile)
700                 render_result_exr_file_begin(re);
701
702         if (type->update) {
703                 type->update(engine, re->main, re->depsgraph, re->scene);
704         }
705
706         /* Clear UI drawing locks. */
707         if (re->draw_lock) {
708                 re->draw_lock(re->dlh, 0);
709         }
710
711         if (type->render_to_image) {
712                 type->render_to_image(engine, re->depsgraph);
713         }
714
715         engine->tile_x = 0;
716         engine->tile_y = 0;
717         engine->flag &= ~RE_ENGINE_RENDERING;
718
719         render_result_free_list(&engine->fullresult, engine->fullresult.first);
720
721         BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE);
722
723         /* re->engine becomes zero if user changed active render engine during render */
724         if (!persistent_data || !re->engine) {
725                 RE_engine_free(engine);
726                 re->engine = NULL;
727         }
728
729         if (re->result->do_exr_tile) {
730                 BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
731                 render_result_save_empty_result_tiles(re);
732                 render_result_exr_file_end(re);
733                 BLI_rw_mutex_unlock(&re->resultmutex);
734         }
735
736         if (re->r.scemode & R_EXR_CACHE_FILE) {
737                 BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
738                 render_result_exr_file_cache_write(re);
739                 BLI_rw_mutex_unlock(&re->resultmutex);
740         }
741
742         RE_parts_free(re);
743         BLI_rw_mutex_unlock(&re->partsmutex);
744
745         if (BKE_reports_contain(re->reports, RPT_ERROR))
746                 G.is_break = true;
747         
748 #ifdef WITH_FREESTYLE
749         if (re->r.mode & R_EDGE_FRS)
750                 RE_RenderFreestyleExternal(re);
751 #endif
752
753         return 1;
754 }
755