diff options
author | Jacques Lucke <jacques@blender.org> | 2021-12-27 19:26:09 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2021-12-27 19:26:09 +0300 |
commit | 1c9d8fcb477c5aea5555781cc209d60da126f48f (patch) | |
tree | 0dfd7e47181b1750d9f6521ef71150ae7178fce5 /source/blender/editors/render/render_internal.c | |
parent | 644e6c7a3e99ae1d43edb25a7d4c3ed86727faba (diff) |
Render: move editor/render module to c++
Doing this in preparation for some work on asset preview generation.
Differential Revision: https://developer.blender.org/D13676
Diffstat (limited to 'source/blender/editors/render/render_internal.c')
-rw-r--r-- | source/blender/editors/render/render_internal.c | 1196 |
1 files changed, 0 insertions, 1196 deletions
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c deleted file mode 100644 index 1a9b72c1fab..00000000000 --- a/source/blender/editors/render/render_internal.c +++ /dev/null @@ -1,1196 +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. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup edrend - */ - -#include <math.h> -#include <stddef.h> -#include <string.h> - -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_rect.h" -#include "BLI_threads.h" -#include "BLI_timecode.h" -#include "BLI_utildefines.h" - -#include "PIL_time.h" - -#include "BLT_translation.h" - -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_userdef_types.h" -#include "DNA_view3d_types.h" - -#include "BKE_colortools.h" -#include "BKE_context.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_node.h" -#include "BKE_node_tree_update.h" -#include "BKE_object.h" -#include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_screen.h" - -#include "DEG_depsgraph.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_render.h" -#include "ED_screen.h" -#include "ED_util.h" - -#include "BIF_glutil.h" - -#include "RE_engine.h" -#include "RE_pipeline.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf_types.h" - -#include "RNA_access.h" -#include "RNA_define.h" - -#include "SEQ_relations.h" - -#include "render_intern.h" - -/* Render Callbacks */ -static int render_break(void *rjv); - -typedef struct RenderJob { - Main *main; - Scene *scene; - ViewLayer *single_layer; - Scene *current_scene; - /* TODO(sergey): Should not be needed once engine will have own - * depsgraph and copy-on-write will be implemented. - */ - Depsgraph *depsgraph; - Render *re; - struct Object *camera_override; - bool v3d_override; - bool anim, write_still; - Image *image; - ImageUser iuser; - bool image_outdated; - short *stop; - short *do_update; - float *progress; - ReportList *reports; - int orig_layer; - int last_layer; - ScrArea *area; - ColorManagedViewSettings view_settings; - ColorManagedDisplaySettings display_settings; - bool supports_glsl_draw; - bool interface_locked; -} RenderJob; - -/* called inside thread! */ -static bool image_buffer_calc_tile_rect(const RenderResult *rr, - const ImBuf *ibuf, - volatile rcti *renrect, - rcti *r_ibuf_rect, - int *r_offset_x, - int *r_offset_y) -{ - int tile_y, tile_height, tile_x, tile_width; - - /* When `renrect` argument is not NULL, we only refresh scan-lines. */ - if (renrect) { - /* if (tile_height == recty), rendering of layer is ready, - * we should not draw, other things happen... */ - if (rr->renlay == NULL || renrect->ymax >= rr->recty) { - return false; - } - - /* tile_x here is first subrect x coord, tile_width defines subrect width */ - tile_x = renrect->xmin; - tile_width = renrect->xmax - tile_x; - if (tile_width < 2) { - return false; - } - - tile_y = renrect->ymin; - tile_height = renrect->ymax - tile_y; - if (tile_height < 2) { - return false; - } - renrect->ymin = renrect->ymax; - } - else { - tile_x = tile_y = 0; - tile_width = rr->rectx; - tile_height = rr->recty; - } - - /* tile_x tile_y is in tile coords. transform to ibuf */ - int offset_x = rr->tilerect.xmin; - if (offset_x >= ibuf->x) { - return false; - } - int offset_y = rr->tilerect.ymin; - if (offset_y >= ibuf->y) { - return false; - } - - if (offset_x + tile_width > ibuf->x) { - tile_width = ibuf->x - offset_x; - } - if (offset_y + tile_height > ibuf->y) { - tile_height = ibuf->y - offset_y; - } - - if (tile_width < 1 || tile_height < 1) { - return false; - } - - r_ibuf_rect->xmax = tile_x + tile_width; - r_ibuf_rect->ymax = tile_y + tile_height; - r_ibuf_rect->xmin = tile_x; - r_ibuf_rect->ymin = tile_y; - *r_offset_x = offset_x; - *r_offset_y = offset_y; - return true; -} - -static void image_buffer_rect_update(RenderJob *rj, - RenderResult *rr, - ImBuf *ibuf, - ImageUser *iuser, - const rcti *tile_rect, - int offset_x, - int offset_y, - const char *viewname) -{ - Scene *scene = rj->scene; - const float *rectf = NULL; - int linear_stride, linear_offset_x, linear_offset_y; - ColorManagedViewSettings *view_settings; - ColorManagedDisplaySettings *display_settings; - - if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) { - /* The whole image buffer is to be color managed again anyway. */ - return; - } - - /* The thing here is, the logic below (which was default behavior - * of how rectf is acquiring since forever) gives float buffer for - * composite output only. This buffer can not be used for other - * passes obviously. - * - * We might try finding corresponding for pass buffer in render result - * (which is actually missing when rendering with Cycles, who only - * writes all the passes when the tile is finished) or use float - * buffer from image buffer as reference, which is easier to use and - * contains all the data we need anyway. - * - sergey - - */ - /* TODO(sergey): Need to check has_combined here? */ - if (iuser->pass == 0) { - RenderView *rv; - const int view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); - rv = RE_RenderViewGetById(rr, view_id); - - /* find current float rect for display, first case is after composite... still weak */ - if (rv->rectf) { - rectf = rv->rectf; - } - else { - if (rv->rect32) { - /* special case, currently only happens with sequencer rendering, - * which updates the whole frame, so we can only mark display buffer - * as invalid here (sergey) - */ - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - return; - } - if (rr->renlay == NULL) { - return; - } - rectf = RE_RenderLayerGetPass(rr->renlay, RE_PASSNAME_COMBINED, viewname); - } - if (rectf == NULL) { - return; - } - - rectf += 4 * (rr->rectx * tile_rect->ymin + tile_rect->xmin); - linear_stride = rr->rectx; - linear_offset_x = offset_x; - linear_offset_y = offset_y; - } - else { - rectf = ibuf->rect_float; - linear_stride = ibuf->x; - linear_offset_x = 0; - linear_offset_y = 0; - } - - view_settings = &scene->view_settings; - display_settings = &scene->display_settings; - - IMB_partial_display_buffer_update(ibuf, - rectf, - NULL, - linear_stride, - linear_offset_x, - linear_offset_y, - view_settings, - display_settings, - offset_x, - offset_y, - offset_x + BLI_rcti_size_x(tile_rect), - offset_y + BLI_rcti_size_y(tile_rect)); -} - -/* ****************************** render invoking ***************** */ - -/* set callbacks, exported to sequence render too. - * Only call in foreground (UI) renders. */ - -static void screen_render_single_layer_set( - wmOperator *op, Main *mainp, ViewLayer *active_layer, Scene **scene, ViewLayer **single_layer) -{ - /* single layer re-render */ - if (RNA_struct_property_is_set(op->ptr, "scene")) { - Scene *scn; - char scene_name[MAX_ID_NAME - 2]; - - RNA_string_get(op->ptr, "scene", scene_name); - scn = (Scene *)BLI_findstring(&mainp->scenes, scene_name, offsetof(ID, name) + 2); - - if (scn) { - /* camera switch won't have updated */ - scn->r.cfra = (*scene)->r.cfra; - BKE_scene_camera_switch_update(scn); - - *scene = scn; - } - } - - if (RNA_struct_property_is_set(op->ptr, "layer")) { - ViewLayer *rl; - char rl_name[RE_MAXNAME]; - - RNA_string_get(op->ptr, "layer", rl_name); - rl = (ViewLayer *)BLI_findstring(&(*scene)->view_layers, rl_name, offsetof(ViewLayer, name)); - - if (rl) { - *single_layer = rl; - } - } - else if (((*scene)->r.scemode & R_SINGLE_LAYER) && active_layer) { - *single_layer = active_layer; - } -} - -/* executes blocking render */ -static int screen_render_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - RenderEngineType *re_type = RE_engines_find(scene->r.engine); - ViewLayer *active_layer = CTX_data_view_layer(C); - ViewLayer *single_layer = NULL; - Render *re; - Image *ima; - View3D *v3d = CTX_wm_view3d(C); - Main *mainp = CTX_data_main(C); - const bool is_animation = RNA_boolean_get(op->ptr, "animation"); - const bool is_write_still = RNA_boolean_get(op->ptr, "write_still"); - struct Object *camera_override = v3d ? V3D_CAMERA_LOCAL(v3d) : NULL; - - /* Cannot do render if there is not this function. */ - if (re_type->render == NULL) { - return OPERATOR_CANCELLED; - } - - /* custom scene and single layer re-render */ - screen_render_single_layer_set(op, mainp, active_layer, &scene, &single_layer); - - if (!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.im_format.imtype)) { - BKE_report( - op->reports, RPT_ERROR, "Cannot write a single file with an animation format selected"); - return OPERATOR_CANCELLED; - } - - re = RE_NewSceneRender(scene); - - G.is_break = false; - - RE_draw_lock_cb(re, NULL, NULL); - RE_test_break_cb(re, NULL, render_break); - - ima = BKE_image_ensure_viewer(mainp, IMA_TYPE_R_RESULT, "Render Result"); - BKE_image_signal(mainp, ima, NULL, IMA_SIGNAL_FREE); - BKE_image_backup_render(scene, ima, true); - - /* cleanup sequencer caches before starting user triggered render. - * otherwise, invalidated cache entries can make their way into - * the output rendering. We can't put that into RE_RenderFrame, - * since sequence rendering can call that recursively... (peter) */ - SEQ_cache_cleanup(scene); - - RE_SetReports(re, op->reports); - - if (is_animation) { - RE_RenderAnim(re, - mainp, - scene, - single_layer, - camera_override, - scene->r.sfra, - scene->r.efra, - scene->r.frame_step); - } - else { - RE_RenderFrame(re, mainp, scene, single_layer, camera_override, scene->r.cfra, is_write_still); - } - - RE_SetReports(re, NULL); - - /* No redraw needed, we leave state as we entered it. */ - ED_update_for_newframe(mainp, CTX_data_depsgraph_pointer(C)); - - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene); - - return OPERATOR_FINISHED; -} - -static void render_freejob(void *rjv) -{ - RenderJob *rj = rjv; - - BKE_color_managed_view_settings_free(&rj->view_settings); - MEM_freeN(rj); -} - -/* str is IMA_MAX_RENDER_TEXT in size */ -static void make_renderinfo_string(const RenderStats *rs, - const Scene *scene, - const bool v3d_override, - const char *error, - char *str) -{ - char info_time_str[32]; /* used to be extern to header_info.c */ - uintptr_t mem_in_use, peak_memory; - float megs_used_memory, megs_peak_memory; - char *spos = str; - - mem_in_use = MEM_get_memory_in_use(); - peak_memory = MEM_get_peak_memory(); - - megs_used_memory = (mem_in_use) / (1024.0 * 1024.0); - megs_peak_memory = (peak_memory) / (1024.0 * 1024.0); - - /* local view */ - if (rs->localview) { - spos += sprintf(spos, "%s | ", TIP_("3D Local View")); - } - else if (v3d_override) { - spos += sprintf(spos, "%s | ", TIP_("3D View")); - } - - /* frame number */ - spos += sprintf(spos, TIP_("Frame:%d "), (scene->r.cfra)); - - /* previous and elapsed time */ - BLI_timecode_string_from_time_simple(info_time_str, sizeof(info_time_str), rs->lastframetime); - - if (rs->infostr && rs->infostr[0]) { - if (rs->lastframetime != 0.0) { - spos += sprintf(spos, TIP_("| Last:%s "), info_time_str); - } - else { - spos += sprintf(spos, "| "); - } - - BLI_timecode_string_from_time_simple( - info_time_str, sizeof(info_time_str), PIL_check_seconds_timer() - rs->starttime); - } - else { - spos += sprintf(spos, "| "); - } - - spos += sprintf(spos, TIP_("Time:%s "), info_time_str); - - /* statistics */ - if (rs->statstr) { - if (rs->statstr[0]) { - spos += sprintf(spos, "| %s ", rs->statstr); - } - } - else { - if (rs->mem_peak == 0.0f) { - spos += sprintf(spos, TIP_("| Mem:%.2fM (Peak %.2fM) "), megs_used_memory, megs_peak_memory); - } - else { - spos += sprintf(spos, TIP_("| Mem:%.2fM, Peak: %.2fM "), rs->mem_used, rs->mem_peak); - } - } - - /* extra info */ - if (rs->infostr && rs->infostr[0]) { - spos += sprintf(spos, "| %s ", rs->infostr); - } - else if (error && error[0]) { - spos += sprintf(spos, "| %s ", error); - } - - /* very weak... but 512 characters is quite safe */ - if (spos >= str + IMA_MAX_RENDER_TEXT) { - if (G.debug & G_DEBUG) { - printf("WARNING! renderwin text beyond limit\n"); - } - } -} - -static void image_renderinfo_cb(void *rjv, RenderStats *rs) -{ - RenderJob *rj = rjv; - RenderResult *rr; - - rr = RE_AcquireResultRead(rj->re); - - if (rr) { - /* malloc OK here, stats_draw is not in tile threads */ - if (rr->text == NULL) { - rr->text = MEM_callocN(IMA_MAX_RENDER_TEXT, "rendertext"); - } - - make_renderinfo_string(rs, rj->scene, rj->v3d_override, rr->error, rr->text); - } - - RE_ReleaseResult(rj->re); - - /* make jobs timer to send notifier */ - *(rj->do_update) = true; -} - -static void render_progress_update(void *rjv, float progress) -{ - RenderJob *rj = rjv; - - if (rj->progress && *rj->progress != progress) { - *rj->progress = progress; - - /* make jobs timer to send notifier */ - *(rj->do_update) = true; - } -} - -/* Not totally reliable, but works fine in most of cases and - * in worst case would just make it so extra color management - * for the whole render result is applied (which was already - * happening already). - */ -static void render_image_update_pass_and_layer(RenderJob *rj, RenderResult *rr, ImageUser *iuser) -{ - wmWindowManager *wm; - ScrArea *first_area = NULL, *matched_area = NULL; - - /* image window, compo node users */ - for (wm = rj->main->wm.first; wm && matched_area == NULL; wm = wm->id.next) { /* only 1 wm */ - wmWindow *win; - for (win = wm->windows.first; win && matched_area == NULL; win = win->next) { - const bScreen *screen = WM_window_get_active_screen(win); - - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (area->spacetype == SPACE_IMAGE) { - SpaceImage *sima = area->spacedata.first; - /* area->spacedata might be empty when toggling full-screen mode. */ - if (sima != NULL && sima->image == rj->image) { - if (first_area == NULL) { - first_area = area; - } - if (area == rj->area) { - matched_area = area; - break; - } - } - } - } - } - } - - if (matched_area == NULL) { - matched_area = first_area; - } - - if (matched_area) { - SpaceImage *sima = matched_area->spacedata.first; - RenderResult *main_rr = RE_AcquireResultRead(rj->re); - - /* TODO(sergey): is there faster way to get the layer index? */ - if (rr->renlay) { - int layer = BLI_findstringindex( - &main_rr->layers, (char *)rr->renlay->name, offsetof(RenderLayer, name)); - sima->iuser.layer = layer; - rj->last_layer = layer; - } - - iuser->pass = sima->iuser.pass; - iuser->layer = sima->iuser.layer; - - RE_ReleaseResult(rj->re); - } -} - -static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect) -{ - RenderJob *rj = rjv; - Image *ima = rj->image; - ImBuf *ibuf; - void *lock; - const char *viewname = RE_GetActiveRenderView(rj->re); - - /* only update if we are displaying the slot being rendered */ - if (ima->render_slot != ima->last_render_slot) { - rj->image_outdated = true; - return; - } - if (rj->image_outdated) { - /* Free all render buffer caches when switching slots, with lock to ensure main - * thread is not drawing the buffer at the same time. */ - rj->image_outdated = false; - ibuf = BKE_image_acquire_ibuf(ima, &rj->iuser, &lock); - BKE_image_free_buffers(ima); - BKE_image_release_ibuf(ima, ibuf, lock); - *(rj->do_update) = true; - return; - } - - if (rr == NULL) { - return; - } - - /* update part of render */ - render_image_update_pass_and_layer(rj, rr, &rj->iuser); - rcti tile_rect; - int offset_x; - int offset_y; - ibuf = BKE_image_acquire_ibuf(ima, &rj->iuser, &lock); - if (ibuf) { - if (!image_buffer_calc_tile_rect(rr, ibuf, renrect, &tile_rect, &offset_x, &offset_y)) { - BKE_image_release_ibuf(ima, ibuf, lock); - return; - } - - /* Don't waste time on CPU side color management if - * image will be displayed using GLSL. - * - * Need to update rect if Save Buffers enabled because in - * this case GLSL doesn't have original float buffer to - * operate with. - */ - if (!rj->supports_glsl_draw || ibuf->channels == 1 || - ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL) { - image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, &tile_rect, offset_x, offset_y, viewname); - } - BKE_image_update_gputexture_delayed( - ima, ibuf, offset_x, offset_y, BLI_rcti_size_x(&tile_rect), BLI_rcti_size_y(&tile_rect)); - - /* make jobs timer to send notifier */ - *(rj->do_update) = true; - } - BKE_image_release_ibuf(ima, ibuf, lock); -} - -static void current_scene_update(void *rjv, Scene *scene) -{ - RenderJob *rj = rjv; - rj->current_scene = scene; - rj->iuser.scene = scene; -} - -static void render_startjob(void *rjv, short *stop, short *do_update, float *progress) -{ - RenderJob *rj = rjv; - - rj->stop = stop; - rj->do_update = do_update; - rj->progress = progress; - - RE_SetReports(rj->re, rj->reports); - - if (rj->anim) { - RE_RenderAnim(rj->re, - rj->main, - rj->scene, - rj->single_layer, - rj->camera_override, - rj->scene->r.sfra, - rj->scene->r.efra, - rj->scene->r.frame_step); - } - else { - RE_RenderFrame(rj->re, - rj->main, - rj->scene, - rj->single_layer, - rj->camera_override, - rj->scene->r.cfra, - rj->write_still); - } - - RE_SetReports(rj->re, NULL); -} - -static void render_image_restore_layer(RenderJob *rj) -{ - wmWindowManager *wm; - - /* image window, compo node users */ - for (wm = rj->main->wm.first; wm; wm = wm->id.next) { /* only 1 wm */ - wmWindow *win; - for (win = wm->windows.first; win; win = win->next) { - const bScreen *screen = WM_window_get_active_screen(win); - - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (area == rj->area) { - if (area->spacetype == SPACE_IMAGE) { - SpaceImage *sima = area->spacedata.first; - - if (RE_HasSingleLayer(rj->re)) { - /* For single layer renders keep the active layer - * visible, or show the compositing result. */ - RenderResult *rr = RE_AcquireResultRead(rj->re); - if (RE_HasCombinedLayer(rr)) { - sima->iuser.layer = 0; - } - RE_ReleaseResult(rj->re); - } - else { - /* For multiple layer render, set back the layer - * that was set at the start of rendering. */ - sima->iuser.layer = rj->orig_layer; - } - } - return; - } - } - } - } -} - -static void render_endjob(void *rjv) -{ - RenderJob *rj = rjv; - - /* this render may be used again by the sequencer without the active - * 'Render' where the callbacks would be re-assigned. assign dummy callbacks - * to avoid referencing freed renderjobs bug T24508. */ - RE_InitRenderCB(rj->re); - - if (rj->main != G_MAIN) { - BKE_main_free(rj->main); - } - - /* else the frame will not update for the original value */ - if (rj->anim && !(rj->scene->r.scemode & R_NO_FRAME_UPDATE)) { - /* possible this fails of loading new file while rendering */ - if (G_MAIN->wm.first) { - ED_update_for_newframe(G_MAIN, rj->depsgraph); - } - } - - /* XXX above function sets all tags in nodes */ - ntreeCompositClearTags(rj->scene->nodetree); - - /* potentially set by caller */ - rj->scene->r.scemode &= ~R_NO_FRAME_UPDATE; - - if (rj->single_layer) { - BKE_ntree_update_tag_id_changed(rj->main, &rj->scene->id); - BKE_ntree_update_main(rj->main, NULL); - WM_main_add_notifier(NC_NODE | NA_EDITED, rj->scene); - } - - if (rj->area) { - render_image_restore_layer(rj); - } - - /* XXX render stability hack */ - G.is_rendering = false; - WM_main_add_notifier(NC_SCENE | ND_RENDER_RESULT, NULL); - - /* Partial render result will always update display buffer - * for first render layer only. This is nice because you'll - * see render progress during rendering, but it ends up in - * wrong display buffer shown after rendering. - * - * The code below will mark display buffer as invalid after - * rendering in case multiple layers were rendered, which - * ensures display buffer matches render layer after - * rendering. - * - * Perhaps proper way would be to toggle active render - * layer in image editor and job, so we always display - * layer being currently rendered. But this is not so much - * trivial at this moment, especially because of external - * engine API, so lets use simple and robust way for now - * - sergey - - */ - if (rj->scene->view_layers.first != rj->scene->view_layers.last || rj->image_outdated) { - void *lock; - Image *ima = rj->image; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &rj->iuser, &lock); - - if (ibuf) { - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - } - - BKE_image_release_ibuf(ima, ibuf, lock); - } - - /* Finally unlock the user interface (if it was locked). */ - if (rj->interface_locked) { - /* Interface was locked, so window manager couldn't have been changed - * and using one from Global will unlock exactly the same manager as - * was locked before running the job. - */ - WM_set_locked_interface(G_MAIN->wm.first, false); - DEG_tag_on_visible_update(G_MAIN, false); - } -} - -/* called by render, check job 'stop' value or the global */ -static int render_breakjob(void *rjv) -{ - RenderJob *rj = rjv; - - if (G.is_break) { - return 1; - } - if (rj->stop && *(rj->stop)) { - return 1; - } - return 0; -} - -/** - * For exec() when there is no render job - * NOTE: this won't check for the escape key being pressed, but doing so isn't thread-safe. - */ -static int render_break(void *UNUSED(rjv)) -{ - if (G.is_break) { - return 1; - } - return 0; -} - -/* runs in thread, no cursor setting here works. careful with notifiers too (malloc conflicts) */ -/* maybe need a way to get job send notifier? */ -static void render_drawlock(void *rjv, bool lock) -{ - RenderJob *rj = rjv; - - /* If interface is locked, renderer callback shall do nothing. */ - if (!rj->interface_locked) { - BKE_spacedata_draw_locks(lock); - } -} - -/* catch esc */ -static int screen_render_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - Scene *scene = (Scene *)op->customdata; - - /* no running blender, remove handler and pass through */ - if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_RENDER)) { - return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; - } - - /* running render */ - switch (event->type) { - case EVT_ESCKEY: - return OPERATOR_RUNNING_MODAL; - } - return OPERATOR_PASS_THROUGH; -} - -static void screen_render_cancel(bContext *C, wmOperator *op) -{ - wmWindowManager *wm = CTX_wm_manager(C); - Scene *scene = (Scene *)op->customdata; - - /* kill on cancel, because job is using op->reports */ - WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_RENDER); -} - -static void clean_viewport_memory_base(Base *base) -{ - if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) { - return; - } - - Object *object = base->object; - - if (object->id.tag & LIB_TAG_DOIT) { - return; - } - - object->id.tag &= ~LIB_TAG_DOIT; - if (RE_allow_render_generic_object(object)) { - BKE_object_free_derived_caches(object); - } -} - -static void clean_viewport_memory(Main *bmain, Scene *scene) -{ - Scene *sce_iter; - Base *base; - - /* Tag all the available objects. */ - BKE_main_id_tag_listbase(&bmain->objects, LIB_TAG_DOIT, true); - - /* Go over all the visible objects. */ - for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { - LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - ViewLayer *view_layer = WM_window_get_active_view_layer(win); - - for (base = view_layer->object_bases.first; base; base = base->next) { - clean_viewport_memory_base(base); - } - } - } - - for (SETLOOPER_SET_ONLY(scene, sce_iter, base)) { - clean_viewport_memory_base(base); - } -} - -/* using context, starts job */ -static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - /* new render clears all callbacks */ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *active_layer = CTX_data_view_layer(C); - ViewLayer *single_layer = NULL; - RenderEngineType *re_type = RE_engines_find(scene->r.engine); - Render *re; - wmJob *wm_job; - RenderJob *rj; - Image *ima; - const bool is_animation = RNA_boolean_get(op->ptr, "animation"); - const bool is_write_still = RNA_boolean_get(op->ptr, "write_still"); - const bool use_viewport = RNA_boolean_get(op->ptr, "use_viewport"); - View3D *v3d = use_viewport ? CTX_wm_view3d(C) : NULL; - struct Object *camera_override = v3d ? V3D_CAMERA_LOCAL(v3d) : NULL; - const char *name; - ScrArea *area; - - /* Cannot do render if there is not this function. */ - if (re_type->render == NULL) { - return OPERATOR_CANCELLED; - } - - /* custom scene and single layer re-render */ - screen_render_single_layer_set(op, bmain, active_layer, &scene, &single_layer); - - /* only one render job at a time */ - if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_RENDER)) { - return OPERATOR_CANCELLED; - } - - if (!RE_is_rendering_allowed(scene, single_layer, camera_override, op->reports)) { - return OPERATOR_CANCELLED; - } - - if (!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.im_format.imtype)) { - BKE_report( - op->reports, RPT_ERROR, "Cannot write a single file with an animation format selected"); - return OPERATOR_CANCELLED; - } - - /* Reports are done inside check function, and it will return false if there are other strips to - * render. */ - if ((scene->r.scemode & R_DOSEQ) && SEQ_relations_check_scene_recursion(scene, op->reports)) { - return OPERATOR_CANCELLED; - } - - /* stop all running jobs, except screen one. currently previews frustrate Render */ - WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(C)); - - /* cancel animation playback */ - if (ED_screen_animation_playing(CTX_wm_manager(C))) { - ED_screen_animation_play(C, 0, 0); - } - - /* handle UI stuff */ - WM_cursor_wait(true); - - /* flush sculpt and editmode changes */ - ED_editors_flush_edits_ex(bmain, true, false); - - /* cleanup sequencer caches before starting user triggered render. - * otherwise, invalidated cache entries can make their way into - * the output rendering. We can't put that into RE_RenderFrame, - * since sequence rendering can call that recursively... (peter) */ - SEQ_cache_cleanup(scene); - - /* store spare - * get view3d layer, local layer, make this nice api call to render - * store spare */ - - /* ensure at least 1 area shows result */ - area = render_view_open(C, event->xy[0], event->xy[1], op->reports); - - /* job custom data */ - rj = MEM_callocN(sizeof(RenderJob), "render job"); - rj->main = bmain; - rj->scene = scene; - rj->current_scene = rj->scene; - rj->single_layer = single_layer; - /* TODO(sergey): Render engine should be using own depsgraph. - * - * NOTE: Currently is only used by ED_update_for_newframe() at the end of the render, so no - * need to ensure evaluation here. */ - rj->depsgraph = CTX_data_depsgraph_pointer(C); - rj->camera_override = camera_override; - rj->anim = is_animation; - rj->write_still = is_write_still && !is_animation; - rj->iuser.scene = scene; - rj->reports = op->reports; - rj->orig_layer = 0; - rj->last_layer = 0; - rj->area = area; - rj->supports_glsl_draw = IMB_colormanagement_support_glsl_draw(&scene->view_settings); - - BKE_color_managed_display_settings_copy(&rj->display_settings, &scene->display_settings); - BKE_color_managed_view_settings_copy(&rj->view_settings, &scene->view_settings); - - if (area) { - SpaceImage *sima = area->spacedata.first; - rj->orig_layer = sima->iuser.layer; - } - - if (v3d) { - if (camera_override && camera_override != scene->camera) { - rj->v3d_override = true; - } - } - - /* Lock the user interface depending on render settings. */ - if (scene->r.use_lock_interface) { - WM_set_locked_interface(CTX_wm_manager(C), true); - - /* Set flag interface need to be unlocked. - * - * This is so because we don't have copy of render settings - * accessible from render job and copy is needed in case - * of non-locked rendering, so we wouldn't try to unlock - * anything if option was initially unset but then was - * enabled during rendering. - */ - rj->interface_locked = true; - - /* Clean memory used by viewport? */ - clean_viewport_memory(rj->main, scene); - } - - /* setup job */ - if (RE_seq_render_active(scene, &scene->r)) { - name = "Sequence Render"; - } - else { - name = "Render"; - } - - wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - scene, - name, - WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, - WM_JOB_TYPE_RENDER); - WM_jobs_customdata_set(wm_job, rj, render_freejob); - WM_jobs_timer(wm_job, 0.2, NC_SCENE | ND_RENDER_RESULT, 0); - WM_jobs_callbacks(wm_job, render_startjob, NULL, NULL, render_endjob); - - if (RNA_struct_property_is_set(op->ptr, "layer")) { - WM_jobs_delay_start(wm_job, 0.2); - } - - /* get a render result image, and make sure it is empty */ - ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"); - BKE_image_signal(rj->main, ima, NULL, IMA_SIGNAL_FREE); - BKE_image_backup_render(rj->scene, ima, true); - rj->image = ima; - - /* setup new render */ - re = RE_NewSceneRender(scene); - RE_test_break_cb(re, rj, render_breakjob); - RE_draw_lock_cb(re, rj, render_drawlock); - RE_display_update_cb(re, rj, image_rect_update); - RE_current_scene_update_cb(re, rj, current_scene_update); - RE_stats_draw_cb(re, rj, image_renderinfo_cb); - RE_progress_cb(re, rj, render_progress_update); - RE_gl_context_create(re); - - rj->re = re; - G.is_break = false; - - /* store actual owner of job, so modal operator could check for it, - * the reason of this is that active scene could change when rendering - * several layers from compositor T31800. */ - op->customdata = scene; - - WM_jobs_start(CTX_wm_manager(C), wm_job); - - WM_cursor_wait(false); - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene); - - /* we set G.is_rendering here already instead of only in the job, this ensure - * main loop or other scene updates are disabled in time, since they may - * have started before the job thread */ - G.is_rendering = true; - - /* add modal handler for ESC */ - WM_event_add_modal_handler(C, op); - - return OPERATOR_RUNNING_MODAL; -} - -void RENDER_OT_render(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Render"; - ot->description = "Render active scene"; - ot->idname = "RENDER_OT_render"; - - /* api callbacks */ - ot->invoke = screen_render_invoke; - ot->modal = screen_render_modal; - ot->cancel = screen_render_cancel; - ot->exec = screen_render_exec; - - /* This isn't needed, causes failure in background mode. */ -#if 0 - ot->poll = ED_operator_screenactive; -#endif - - prop = RNA_def_boolean(ot->srna, - "animation", - 0, - "Animation", - "Render files from the animation range of this scene"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - RNA_def_boolean( - ot->srna, - "write_still", - 0, - "Write Image", - "Save rendered the image to the output path (used only when animation is disabled)"); - prop = RNA_def_boolean(ot->srna, - "use_viewport", - 0, - "Use 3D Viewport", - "When inside a 3D viewport, use layers and camera of the viewport"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_string(ot->srna, - "layer", - NULL, - RE_MAXNAME, - "Render Layer", - "Single render layer to re-render (used only when animation is disabled)"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_string(ot->srna, - "scene", - NULL, - MAX_ID_NAME - 2, - "Scene", - "Scene to render, current scene if not specified"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -Scene *ED_render_job_get_scene(const bContext *C) -{ - wmWindowManager *wm = CTX_wm_manager(C); - RenderJob *rj = (RenderJob *)WM_jobs_customdata_from_type(wm, WM_JOB_TYPE_RENDER); - - if (rj) { - return rj->scene; - } - - return NULL; -} - -Scene *ED_render_job_get_current_scene(const bContext *C) -{ - wmWindowManager *wm = CTX_wm_manager(C); - RenderJob *rj = (RenderJob *)WM_jobs_customdata_from_type(wm, WM_JOB_TYPE_RENDER); - if (rj) { - return rj->current_scene; - } - return NULL; -} - -/* Motion blur curve preset */ - -static int render_shutter_curve_preset_exec(bContext *C, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - CurveMapping *mblur_shutter_curve = &scene->r.mblur_shutter_curve; - CurveMap *cm = mblur_shutter_curve->cm; - int preset = RNA_enum_get(op->ptr, "shape"); - - mblur_shutter_curve->flag &= ~CUMA_EXTEND_EXTRAPOLATE; - mblur_shutter_curve->preset = preset; - BKE_curvemap_reset( - cm, &mblur_shutter_curve->clipr, mblur_shutter_curve->preset, CURVEMAP_SLOPE_POS_NEG); - BKE_curvemapping_changed(mblur_shutter_curve, false); - - return OPERATOR_FINISHED; -} - -void RENDER_OT_shutter_curve_preset(wmOperatorType *ot) -{ - PropertyRNA *prop; - static const EnumPropertyItem prop_shape_items[] = { - {CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""}, - {CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""}, - {CURVE_PRESET_MAX, "MAX", 0, "Max", ""}, - {CURVE_PRESET_LINE, "LINE", 0, "Line", ""}, - {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""}, - {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""}, - {0, NULL, 0, NULL, NULL}, - }; - - ot->name = "Shutter Curve Preset"; - ot->description = "Set shutter curve"; - ot->idname = "RENDER_OT_shutter_curve_preset"; - - ot->exec = render_shutter_curve_preset_exec; - - prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); - RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ -} |