2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * The Original Code is Copyright (C) 2008 Blender Foundation.
17 * All rights reserved.
29 #include "MEM_guardedalloc.h"
31 #include "DNA_camera_types.h"
33 #include "BLI_math_color_blend.h"
34 #include "BLI_blenlib.h"
35 #include "BLI_utildefines.h"
36 #include "BLI_threads.h"
39 #include "DNA_scene_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_gpencil_types.h"
43 #include "BKE_camera.h"
44 #include "BKE_context.h"
45 #include "BKE_customdata.h"
46 #include "BKE_global.h"
47 #include "BKE_image.h"
49 #include "BKE_report.h"
50 #include "BKE_scene.h"
51 #include "BKE_sequencer.h"
52 #include "BKE_writeavi.h"
54 #include "DEG_depsgraph.h"
56 #include "DRW_engine.h"
61 #include "ED_screen.h"
62 #include "ED_view3d.h"
63 #include "ED_gpencil.h"
65 #include "RE_pipeline.h"
66 #include "IMB_imbuf_types.h"
67 #include "IMB_imbuf.h"
68 #include "IMB_colormanagement.h"
70 #include "RNA_access.h"
71 #include "RNA_define.h"
73 #include "GPU_framebuffer.h"
75 #include "GPU_matrix.h"
77 #include "render_intern.h"
79 /* Define this to get timing information. */
83 # include "PIL_time.h"
86 // TODO(sergey): Find better approximation of the scheduled frames.
87 // For really highres renders it might fail still.
88 #define MAX_SCHEDULED_FRAMES 8
90 typedef struct OGLRender {
95 ViewLayer *view_layer;
105 int views_len; /* multi-view views */
119 bool ofs_full_samples;
132 /* wm vars for timer and progress cursor */
136 wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/
137 void **movie_ctx_arr;
139 TaskScheduler *task_scheduler;
143 SpinLock reports_lock;
144 unsigned int num_scheduled_frames;
145 ThreadMutex task_mutex;
146 ThreadCondition task_condition;
153 static bool screen_opengl_is_multiview(OGLRender *oglrender)
155 View3D *v3d = oglrender->v3d;
156 RegionView3D *rv3d = oglrender->rv3d;
157 RenderData *rd = &oglrender->scene->r;
159 if ((rd == NULL) || ((!oglrender->is_sequencer) && ((rv3d == NULL) || (v3d == NULL))))
162 return (rd->scemode & R_MULTIVIEW) && ((oglrender->is_sequencer) || (rv3d->persp == RV3D_CAMOB && v3d->camera));
165 static void screen_opengl_views_setup(OGLRender *oglrender)
169 SceneRenderView *srv;
172 View3D *v3d = oglrender->v3d;
174 RenderData *rd = &oglrender->scene->r;
176 rr = RE_AcquireResultWrite(oglrender->re);
178 is_multiview = screen_opengl_is_multiview(oglrender);
181 /* we only have one view when multiview is off */
182 rv = rr->views.first;
185 rv = MEM_callocN(sizeof(RenderView), "new opengl render view");
186 BLI_addtail(&rr->views, rv);
190 RenderView *rv_del = rv->next;
191 BLI_remlink(&rr->views, rv_del);
194 MEM_freeN(rv_del->rectf);
197 MEM_freeN(rv_del->rectz);
200 MEM_freeN(rv_del->rect32);
206 if (!oglrender->is_sequencer)
207 RE_SetOverrideCamera(oglrender->re, V3D_CAMERA_SCENE(oglrender->scene, v3d));
209 /* remove all the views that are not needed */
212 srv = BLI_findstring(&rd->views, rv->name, offsetof(SceneRenderView, name));
213 if (BKE_scene_multiview_is_render_view_active(rd, srv)) {
217 RenderView *rv_del = rv;
220 BLI_remlink(&rr->views, rv_del);
223 MEM_freeN(rv_del->rectf);
226 MEM_freeN(rv_del->rectz);
229 MEM_freeN(rv_del->rect32);
235 /* create all the views that are needed */
236 for (srv = rd->views.first; srv; srv = srv->next) {
237 if (BKE_scene_multiview_is_render_view_active(rd, srv) == false)
240 rv = BLI_findstring(&rr->views, srv->name, offsetof(SceneRenderView, name));
243 rv = MEM_callocN(sizeof(RenderView), "new opengl render view");
244 BLI_strncpy(rv->name, srv->name, sizeof(rv->name));
245 BLI_addtail(&rr->views, rv);
250 if (!(is_multiview && BKE_scene_multiview_is_stereo3d(rd)))
251 oglrender->iuser.flag &= ~IMA_SHOW_STEREO;
253 /* will only work for non multiview correctly */
255 camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, "new opengl render view");
256 BKE_render_result_stamp_info(oglrender->scene, camera, rr, false);
259 BKE_render_result_stamp_info(oglrender->scene, oglrender->scene->camera, rr, false);
262 RE_ReleaseResult(oglrender->re);
265 static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, RenderResult *rr)
267 Depsgraph *depsgraph = CTX_data_depsgraph(C);
268 ViewLayer *view_layer = CTX_data_view_layer(C);
269 Scene *scene = oglrender->scene;
270 ARegion *ar = oglrender->ar;
271 View3D *v3d = oglrender->v3d;
272 RegionView3D *rv3d = oglrender->rv3d;
273 Object *camera = NULL;
274 int sizex = oglrender->sizex;
275 int sizey = oglrender->sizey;
276 const short view_context = (v3d != NULL);
277 bool draw_sky = (scene->r.alphamode == R_ADDSKY);
279 const char *viewname = RE_GetActiveRenderView(oglrender->re);
280 ImBuf *ibuf_result = NULL;
282 if (oglrender->is_sequencer) {
283 SpaceSeq *sseq = oglrender->sseq;
284 struct bGPdata *gpd = (sseq && (sseq->flag & SEQ_SHOW_GPENCIL)) ? sseq->gpd : NULL;
286 /* use pre-calculated ImBuf (avoids deadlock), see: */
287 ImBuf *ibuf = oglrender->seq_data.ibufs_arr[oglrender->view_id];
290 ImBuf *out = IMB_dupImBuf(ibuf);
292 /* OpenGL render is considered to be preview and should be
293 * as fast as possible. So currently we're making sure sequencer
294 * result is always byte to simplify color management pipeline.
296 * TODO(sergey): In the case of output to float container (EXR)
297 * it actually makes sense to keep float buffer instead.
299 if (out->rect_float != NULL) {
300 IMB_rect_from_float(out);
301 imb_freerectfloatImBuf(out);
303 BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y));
304 RE_render_result_rect_from_ibuf(rr, &scene->r, out, oglrender->view_id);
308 /* If there are no strips, Grease Pencil still needs a buffer to draw on */
309 ImBuf *out = IMB_allocImBuf(oglrender->sizex, oglrender->sizey, 32, IB_rect);
310 RE_render_result_rect_from_ibuf(rr, &scene->r, out, oglrender->view_id);
316 unsigned char *gp_rect;
317 unsigned char *render_rect = (unsigned char *)RE_RenderViewGetById(rr, oglrender->view_id)->rect32;
319 DRW_opengl_context_enable();
320 GPU_offscreen_bind(oglrender->ofs, true);
322 GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f);
323 GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT);
325 wmOrtho2(0, sizex, 0, sizey);
326 GPU_matrix_translate_2f(sizex / 2, sizey / 2);
328 G.f |= G_FLAG_RENDER_VIEWPORT;
330 view_layer, rv3d, scene, gpd, sizex, sizey, scene->r.cfra, SPACE_SEQ);
331 G.f &= ~G_FLAG_RENDER_VIEWPORT;
333 gp_rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect");
334 GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect);
336 for (i = 0; i < sizex * sizey * 4; i += 4) {
337 blend_color_mix_byte(&render_rect[i], &render_rect[i], &gp_rect[i]);
339 GPU_offscreen_unbind(oglrender->ofs, true);
340 DRW_opengl_context_disable();
346 /* shouldnt suddenly give errors mid-render but possible */
347 char err_out[256] = "unknown";
349 const int alpha_mode = (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL;
351 unsigned int draw_flags = V3D_OFSDRAW_NONE;
352 draw_flags |= (oglrender->ofs_full_samples) ? V3D_OFSDRAW_USE_FULL_SAMPLE : 0;
355 ibuf_view = ED_view3d_draw_offscreen_imbuf(
356 depsgraph, scene, v3d->shading.type,
357 v3d, ar, sizex, sizey,
358 IB_rectfloat, draw_flags, alpha_mode, oglrender->ofs_samples, viewname,
359 oglrender->ofs, err_out);
362 if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
363 camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname);
367 draw_flags |= V3D_OFSDRAW_USE_GPENCIL;
368 ibuf_view = ED_view3d_draw_offscreen_imbuf_simple(
369 depsgraph, scene, OB_SOLID,
370 scene->camera, oglrender->sizex, oglrender->sizey,
371 IB_rectfloat, draw_flags,
372 alpha_mode, oglrender->ofs_samples, viewname,
373 oglrender->ofs, err_out);
374 camera = scene->camera;
378 ibuf_result = ibuf_view;
379 rectf = (float *)ibuf_view->rect_float;
382 fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out);
386 if (ibuf_result != NULL) {
387 if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) {
388 BKE_image_stamp_buf(scene, camera, NULL, NULL, rectf, rr->rectx, rr->recty, 4);
390 RE_render_result_rect_from_ibuf(rr, &scene->r, ibuf_result, oglrender->view_id);
391 IMB_freeImBuf(ibuf_result);
395 static void screen_opengl_render_write(OGLRender *oglrender)
397 Scene *scene = oglrender->scene;
402 rr = RE_AcquireResultRead(oglrender->re);
404 BKE_image_path_from_imformat(
405 name, scene->r.pic, BKE_main_blendfile_path(oglrender->bmain), scene->r.cfra,
406 &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL);
408 /* write images as individual images or stereo */
409 BKE_render_result_stamp_info(scene, scene->camera, rr, false);
410 ok = RE_WriteRenderViewsImage(oglrender->reports, rr, scene, false, name);
412 RE_ReleaseResultImage(oglrender->re);
414 if (ok) printf("OpenGL Render written to '%s'\n", name);
415 else printf("OpenGL Render failed to write '%s'\n", name);
418 static void UNUSED_FUNCTION(addAlphaOverFloat)(float dest[4], const float source[4])
420 /* d = s + (1-alpha_s)d*/
423 mul = 1.0f - source[3];
425 dest[0] = (mul * dest[0]) + source[0];
426 dest[1] = (mul * dest[1]) + source[1];
427 dest[2] = (mul * dest[2]) + source[2];
428 dest[3] = (mul * dest[3]) + source[3];
432 static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender)
440 if (oglrender->is_sequencer) {
441 Scene *scene = oglrender->scene;
443 SeqRenderData context;
444 SpaceSeq *sseq = oglrender->sseq;
445 int chanshown = sseq ? sseq->chanshown : 0;
447 BKE_sequencer_new_render_data(
448 oglrender->bmain, oglrender->depsgraph, scene,
449 oglrender->sizex, oglrender->sizey, 100.0f, false,
452 for (view_id = 0; view_id < oglrender->views_len; view_id++) {
453 context.view_id = view_id;
454 context.gpu_offscreen = oglrender->ofs;
455 context.gpu_full_samples = oglrender->ofs_full_samples;
457 oglrender->seq_data.ibufs_arr[view_id] = BKE_sequencer_give_ibuf(&context, CFRA, chanshown);
461 rr = RE_AcquireResultRead(oglrender->re);
462 for (rv = rr->views.first, view_id = 0; rv; rv = rv->next, view_id++) {
463 BLI_assert(view_id < oglrender->views_len);
464 RE_SetActiveRenderView(oglrender->re, rv->name);
465 oglrender->view_id = view_id;
466 /* render composite */
467 screen_opengl_render_doit(C, oglrender, rr);
470 RE_ReleaseResult(oglrender->re);
472 ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
474 ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
476 BKE_image_release_ibuf(oglrender->ima, ibuf, lock);
478 if (oglrender->write_still) {
479 screen_opengl_render_write(oglrender);
483 static bool screen_opengl_fullsample_enabled(Scene *scene)
485 if (scene->r.scemode & R_FULL_SAMPLE) {
490 * Technically if the hardware supports MSAA we could keep using Blender 2.7x approach.
491 * However anti-aliasing without full_sample is not playing well even in 2.7x.
493 * For example, if you enable depth of field, there is aliasing, even if the viewport is fine.
494 * For 2.8x this is more complicated because so many things rely on shader.
495 * So until we fix the gpu_framebuffer anti-aliasing suupport we need to force full sample.
501 static bool screen_opengl_render_init(bContext *C, wmOperator *op)
503 /* new render clears all callbacks */
504 wmWindowManager *wm = CTX_wm_manager(C);
505 wmWindow *win = CTX_wm_window(C);
506 WorkSpace *workspace = CTX_wm_workspace(C);
508 Scene *scene = CTX_data_scene(C);
509 ScrArea *prevsa = CTX_wm_area(C);
510 ARegion *prevar = CTX_wm_region(C);
512 OGLRender *oglrender;
514 const int samples = (scene->r.mode & R_OSA) ? scene->r.osa : 0;
515 const bool full_samples = (samples != 0) && screen_opengl_fullsample_enabled(scene);
516 bool is_view_context = RNA_boolean_get(op->ptr, "view_context");
517 const bool is_animation = RNA_boolean_get(op->ptr, "animation");
518 const bool is_sequencer = RNA_boolean_get(op->ptr, "sequencer");
519 const bool is_write_still = RNA_boolean_get(op->ptr, "write_still");
520 char err_out[256] = "unknown";
523 BKE_report(op->reports, RPT_ERROR, "Cannot use OpenGL render in background mode (no opengl context)");
527 /* only one render job at a time */
528 if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER))
532 is_view_context = false;
535 /* ensure we have a 3d view */
536 if (!ED_view3d_context_activate(C)) {
537 RNA_boolean_set(op->ptr, "view_context", false);
538 is_view_context = false;
541 if (!is_view_context && scene->camera == NULL) {
542 BKE_report(op->reports, RPT_ERROR, "Scene has no camera");
547 if (!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.im_format.imtype)) {
548 BKE_report(op->reports, RPT_ERROR, "Cannot write a single file with an animation format selected");
552 /* stop all running jobs, except screen one. currently previews frustrate Render */
553 WM_jobs_kill_all_except(wm, CTX_wm_screen(C));
555 /* create offscreen buffer */
556 sizex = (scene->r.size * scene->r.xsch) / 100;
557 sizey = (scene->r.size * scene->r.ysch) / 100;
559 /* corrects render size with actual size, not every card supports non-power-of-two dimensions */
560 DRW_opengl_context_enable(); /* Offscreen creation needs to be done in DRW context. */
561 ofs = GPU_offscreen_create(sizex, sizey, full_samples ? 0 : samples, true, true, err_out);
562 DRW_opengl_context_disable();
565 BKE_reportf(op->reports, RPT_ERROR, "Failed to create OpenGL off-screen buffer, %s", err_out);
569 /* allocate opengl render */
570 oglrender = MEM_callocN(sizeof(OGLRender), "OGLRender");
571 op->customdata = oglrender;
573 oglrender->ofs = ofs;
574 oglrender->ofs_samples = samples;
575 oglrender->ofs_full_samples = full_samples;
576 oglrender->sizex = sizex;
577 oglrender->sizey = sizey;
578 oglrender->bmain = CTX_data_main(C);
579 oglrender->scene = scene;
580 oglrender->workspace = workspace;
581 oglrender->view_layer = CTX_data_view_layer(C);
582 oglrender->depsgraph = CTX_data_depsgraph(C);
583 oglrender->cfrao = scene->r.cfra;
585 oglrender->write_still = is_write_still && !is_animation;
586 oglrender->is_animation = is_animation;
588 oglrender->views_len = BKE_scene_multiview_num_views_get(&scene->r);
590 oglrender->is_sequencer = is_sequencer;
592 oglrender->sseq = CTX_wm_space_seq(C);
593 ImBuf **ibufs_arr = MEM_callocN(sizeof(*ibufs_arr) * oglrender->views_len, __func__);
594 oglrender->seq_data.ibufs_arr = ibufs_arr;
597 oglrender->prevsa = prevsa;
598 oglrender->prevar = prevar;
600 if (is_view_context) {
601 /* so quad view renders camera */
602 ED_view3d_context_user_region(C, &oglrender->v3d, &oglrender->ar);
604 oglrender->rv3d = oglrender->ar->regiondata;
606 /* MUST be cleared on exit */
607 memset(&oglrender->scene->customdata_mask_modal, 0, sizeof(oglrender->scene->customdata_mask_modal));
608 ED_view3d_datamask(C, oglrender->scene, oglrender->v3d, &oglrender->scene->customdata_mask_modal);
610 /* apply immediately in case we're rendering from a script,
611 * running notifiers again will overwrite */
612 CustomData_MeshMasks_update(&oglrender->scene->customdata_mask, &oglrender->scene->customdata_mask_modal);
616 oglrender->re = RE_NewSceneRender(scene);
618 /* create image and image user */
619 oglrender->ima = BKE_image_verify_viewer(oglrender->bmain, IMA_TYPE_R_RESULT, "Render Result");
620 BKE_image_signal(oglrender->bmain, oglrender->ima, NULL, IMA_SIGNAL_FREE);
621 BKE_image_backup_render(oglrender->scene, oglrender->ima, true);
623 oglrender->iuser.scene = scene;
624 oglrender->iuser.ok = 1;
626 /* create render result */
627 RE_InitState(oglrender->re, NULL, &scene->r, &scene->view_layers, NULL, sizex, sizey, NULL);
629 /* create render views */
630 screen_opengl_views_setup(oglrender);
634 oglrender->win = win;
636 oglrender->totvideos = 0;
637 oglrender->mh = NULL;
638 oglrender->movie_ctx_arr = NULL;
641 TaskScheduler *task_scheduler = BLI_task_scheduler_get();
642 if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
643 task_scheduler = BLI_task_scheduler_create(1);
644 oglrender->task_scheduler = task_scheduler;
645 oglrender->task_pool = BLI_task_pool_create_background(task_scheduler,
649 oglrender->task_scheduler = NULL;
650 oglrender->task_pool = BLI_task_pool_create(task_scheduler,
653 oglrender->pool_ok = true;
654 BLI_spin_init(&oglrender->reports_lock);
657 oglrender->task_scheduler = NULL;
658 oglrender->task_pool = NULL;
660 oglrender->num_scheduled_frames = 0;
661 BLI_mutex_init(&oglrender->task_mutex);
662 BLI_condition_init(&oglrender->task_condition);
665 oglrender->time_start = PIL_check_seconds_timer();
671 static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
673 Main *bmain = CTX_data_main(C);
674 Scene *scene = oglrender->scene;
677 if (oglrender->is_animation) {
678 /* Trickery part for movie output:
680 * We MUST write frames in an exact order, so we only let background
681 * thread to work on that, and main thread is simply waits for that
682 * thread to do all the dirty work.
684 * After this loop is done work_and_wait() will have nothing to do,
685 * so we don't run into wrong order of frames written to the stream.
687 if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
688 BLI_mutex_lock(&oglrender->task_mutex);
689 while (oglrender->num_scheduled_frames > 0) {
690 BLI_condition_wait(&oglrender->task_condition,
691 &oglrender->task_mutex);
693 BLI_mutex_unlock(&oglrender->task_mutex);
695 BLI_task_pool_work_and_wait(oglrender->task_pool);
696 BLI_task_pool_free(oglrender->task_pool);
697 /* Depending on various things we might or might not use global scheduler. */
698 if (oglrender->task_scheduler != NULL) {
699 BLI_task_scheduler_free(oglrender->task_scheduler);
701 BLI_spin_end(&oglrender->reports_lock);
703 BLI_mutex_end(&oglrender->task_mutex);
704 BLI_condition_end(&oglrender->task_condition);
707 printf("Total render time: %f\n", PIL_check_seconds_timer() - oglrender->time_start);
711 if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
712 for (i = 0; i < oglrender->totvideos; i++) {
713 oglrender->mh->end_movie(oglrender->movie_ctx_arr[i]);
714 oglrender->mh->context_free(oglrender->movie_ctx_arr[i]);
718 if (oglrender->movie_ctx_arr) {
719 MEM_freeN(oglrender->movie_ctx_arr);
723 if (oglrender->timer) { /* exec will not have a timer */
724 Depsgraph *depsgraph = oglrender->depsgraph;
725 scene->r.cfra = oglrender->cfrao;
726 BKE_scene_graph_update_for_newframe(depsgraph, bmain);
728 WM_event_remove_timer(oglrender->wm, oglrender->win, oglrender->timer);
731 WM_cursor_modal_restore(oglrender->win);
733 WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene);
735 DRW_opengl_context_enable();
736 GPU_offscreen_free(oglrender->ofs);
737 DRW_opengl_context_disable();
739 if (oglrender->is_sequencer) {
740 MEM_freeN(oglrender->seq_data.ibufs_arr);
743 memset(&oglrender->scene->customdata_mask_modal, 0, sizeof(oglrender->scene->customdata_mask_modal));
745 CTX_wm_area_set(C, oglrender->prevsa);
746 CTX_wm_region_set(C, oglrender->prevar);
748 MEM_freeN(oglrender);
751 static void screen_opengl_render_cancel(bContext *C, wmOperator *op)
753 screen_opengl_render_end(C, op->customdata);
756 /* share between invoke and exec */
757 static bool screen_opengl_render_anim_initialize(bContext *C, wmOperator *op)
759 /* initialize animation */
760 OGLRender *oglrender;
763 oglrender = op->customdata;
764 scene = oglrender->scene;
765 oglrender->totvideos = BKE_scene_multiview_num_videos_get(&scene->r);
767 oglrender->reports = op->reports;
769 if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
770 size_t width, height;
773 BKE_scene_multiview_videos_dimensions_get(&scene->r, oglrender->sizex, oglrender->sizey, &width, &height);
774 oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype);
776 if (oglrender->mh == NULL) {
777 BKE_report(oglrender->reports, RPT_ERROR, "Movie format unsupported");
778 screen_opengl_render_end(C, oglrender);
782 oglrender->movie_ctx_arr = MEM_mallocN(sizeof(void *) * oglrender->totvideos, "Movies");
784 for (i = 0; i < oglrender->totvideos; i++) {
785 const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
787 oglrender->movie_ctx_arr[i] = oglrender->mh->context_create();
788 if (!oglrender->mh->start_movie(oglrender->movie_ctx_arr[i], scene, &scene->r, oglrender->sizex,
789 oglrender->sizey, oglrender->reports, PRVRANGEON != 0, suffix))
791 screen_opengl_render_end(C, oglrender);
797 oglrender->cfrao = scene->r.cfra;
798 oglrender->nfra = PSFRA;
799 scene->r.cfra = PSFRA;
804 typedef struct WriteTaskData {
809 static void write_result_func(TaskPool * __restrict pool,
811 int UNUSED(thread_id))
813 OGLRender *oglrender = (OGLRender *) BLI_task_pool_userdata(pool);
814 WriteTaskData *task_data = (WriteTaskData *) task_data_v;
815 Scene *scene = &task_data->tmp_scene;
816 RenderResult *rr = task_data->rr;
817 const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype);
818 const int cfra = scene->r.cfra;
820 /* Don't attempt to write if we've got an error. */
821 if (!oglrender->pool_ok) {
822 RE_FreeRenderResult(rr);
823 BLI_mutex_lock(&oglrender->task_mutex);
824 oglrender->num_scheduled_frames--;
825 BLI_condition_notify_all(&oglrender->task_condition);
826 BLI_mutex_unlock(&oglrender->task_mutex);
829 /* Construct local thread0safe copy of reports structure which we can
830 * safely pass to the underlying functions.
833 BKE_reports_init(&reports, oglrender->reports->flag & ~RPT_PRINT);
834 /* Do actual save logic here, depending on the file format.
836 * NOTE: We have to construct temporary scene with proper scene->r.cfra.
837 * This is because underlying calls do not use r.cfra but use scene
841 ok = RE_WriteRenderViewsMovie(&reports,
846 oglrender->movie_ctx_arr,
847 oglrender->totvideos,
851 /* TODO(sergey): We can in theory save some CPU ticks here because we
852 * calculate file name again here.
855 BKE_image_path_from_imformat(name,
857 BKE_main_blendfile_path(oglrender->bmain),
860 (scene->r.scemode & R_EXTENSION) != 0,
864 BKE_render_result_stamp_info(scene, scene->camera, rr, false);
865 ok = RE_WriteRenderViewsImage(NULL, rr, scene, true, name);
867 BKE_reportf(&reports,
869 "Write error: cannot save %s",
873 if (reports.list.first != NULL) {
874 BLI_spin_lock(&oglrender->reports_lock);
875 for (Report *report = reports.list.first;
877 report = report->next)
879 BKE_report(oglrender->reports,
883 BLI_spin_unlock(&oglrender->reports_lock);
886 oglrender->pool_ok = false;
888 RE_FreeRenderResult(rr);
889 BLI_mutex_lock(&oglrender->task_mutex);
890 oglrender->num_scheduled_frames--;
891 BLI_condition_notify_all(&oglrender->task_condition);
892 BLI_mutex_unlock(&oglrender->task_mutex);
895 static bool schedule_write_result(OGLRender *oglrender, RenderResult *rr)
897 if (!oglrender->pool_ok) {
898 RE_FreeRenderResult(rr);
901 Scene *scene = oglrender->scene;
902 WriteTaskData *task_data = MEM_mallocN(sizeof(WriteTaskData), "write task data");
904 task_data->tmp_scene = *scene;
905 BLI_mutex_lock(&oglrender->task_mutex);
906 oglrender->num_scheduled_frames++;
907 if (oglrender->num_scheduled_frames > MAX_SCHEDULED_FRAMES) {
908 BLI_condition_wait(&oglrender->task_condition,
909 &oglrender->task_mutex);
911 BLI_mutex_unlock(&oglrender->task_mutex);
912 BLI_task_pool_push(oglrender->task_pool,
920 static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
922 Main *bmain = CTX_data_main(C);
923 OGLRender *oglrender = op->customdata;
924 Scene *scene = oglrender->scene;
925 Depsgraph *depsgraph = oglrender->depsgraph;
928 const bool view_context = (oglrender->v3d != NULL);
932 /* go to next frame */
933 if (CFRA < oglrender->nfra)
935 while (CFRA < oglrender->nfra) {
936 BKE_scene_graph_update_for_newframe(depsgraph, bmain);
940 is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype);
943 BKE_image_path_from_imformat(
944 name, scene->r.pic, BKE_main_blendfile_path(oglrender->bmain), scene->r.cfra,
945 &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL);
947 if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) {
948 BLI_spin_lock(&oglrender->reports_lock);
949 BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name);
950 BLI_spin_unlock(&oglrender->reports_lock);
956 WM_cursor_time(oglrender->win, scene->r.cfra);
958 BKE_scene_graph_update_for_newframe(depsgraph, bmain);
961 if (oglrender->rv3d->persp == RV3D_CAMOB && oglrender->v3d->camera && oglrender->v3d->scenelock) {
962 /* since BKE_scene_graph_update_for_newframe() is used rather
963 * then ED_update_for_newframe() the camera needs to be set */
964 if (BKE_scene_camera_switch_update(scene)) {
965 oglrender->v3d->camera = scene->camera;
970 BKE_scene_camera_switch_update(scene);
973 /* render into offscreen buffer */
974 screen_opengl_render_apply(C, oglrender);
977 rr = RE_AcquireResultRead(oglrender->re);
978 RenderResult *new_rr = RE_DuplicateRenderResult(rr);
979 RE_ReleaseResult(oglrender->re);
981 ok = schedule_write_result(oglrender, new_rr);
983 finally: /* Step the frame and bail early if needed */
985 /* go to next frame */
986 oglrender->nfra += scene->r.frame_step;
988 /* stop at the end or on error */
989 if (CFRA >= PEFRA || !ok) {
990 screen_opengl_render_end(C, op->customdata);
998 static int screen_opengl_render_modal(bContext *C, wmOperator *op, const wmEvent *event)
1000 OGLRender *oglrender = op->customdata;
1001 const bool anim = RNA_boolean_get(op->ptr, "animation");
1004 switch (event->type) {
1007 oglrender->pool_ok = false; /* Flag pool for cancel. */
1008 screen_opengl_render_end(C, op->customdata);
1009 return OPERATOR_FINISHED;
1012 if (oglrender->timer == event->customdata)
1017 return OPERATOR_RUNNING_MODAL;
1020 /* run first because screen_opengl_render_anim_step can free oglrender */
1021 WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene);
1024 screen_opengl_render_apply(C, op->customdata);
1025 screen_opengl_render_end(C, op->customdata);
1026 return OPERATOR_FINISHED;
1029 ret = screen_opengl_render_anim_step(C, op);
1032 /* stop at the end or on error */
1034 return OPERATOR_FINISHED;
1037 return OPERATOR_RUNNING_MODAL;
1040 static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1042 OGLRender *oglrender;
1043 const bool anim = RNA_boolean_get(op->ptr, "animation");
1045 if (!screen_opengl_render_init(C, op))
1046 return OPERATOR_CANCELLED;
1049 if (!screen_opengl_render_anim_initialize(C, op))
1050 return OPERATOR_CANCELLED;
1053 oglrender = op->customdata;
1054 render_view_open(C, event->x, event->y, op->reports);
1056 /* view may be changed above (R_OUTPUT_WINDOW) */
1057 oglrender->win = CTX_wm_window(C);
1059 WM_event_add_modal_handler(C, op);
1060 oglrender->timer = WM_event_add_timer(oglrender->wm, oglrender->win, TIMER, 0.01f);
1062 return OPERATOR_RUNNING_MODAL;
1065 /* executes blocking render */
1066 static int screen_opengl_render_exec(bContext *C, wmOperator *op)
1068 const bool is_animation = RNA_boolean_get(op->ptr, "animation");
1070 if (!screen_opengl_render_init(C, op))
1071 return OPERATOR_CANCELLED;
1073 if (!is_animation) { /* same as invoke */
1075 screen_opengl_render_apply(C, op->customdata);
1076 screen_opengl_render_end(C, op->customdata);
1078 return OPERATOR_FINISHED;
1083 if (!screen_opengl_render_anim_initialize(C, op))
1084 return OPERATOR_CANCELLED;
1087 ret = screen_opengl_render_anim_step(C, op);
1091 /* no redraw needed, we leave state as we entered it */
1092 // ED_update_for_newframe(C);
1093 WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, CTX_data_scene(C));
1095 return OPERATOR_FINISHED;
1098 void RENDER_OT_opengl(wmOperatorType *ot)
1103 ot->name = "Viewport Render";
1104 ot->description = "Take a snapshot of the active viewport";
1105 ot->idname = "RENDER_OT_opengl";
1108 ot->invoke = screen_opengl_render_invoke;
1109 ot->exec = screen_opengl_render_exec; /* blocking */
1110 ot->modal = screen_opengl_render_modal;
1111 ot->cancel = screen_opengl_render_cancel;
1113 ot->poll = ED_operator_screenactive;
1115 prop = RNA_def_boolean(ot->srna, "animation", 0, "Animation", "Render files from the animation range of this scene");
1116 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1117 prop = RNA_def_boolean(ot->srna, "sequencer", 0, "Sequencer", "Render using the sequencer's OpenGL display");
1118 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1119 prop = RNA_def_boolean(ot->srna, "write_still", 0, "Write Image", "Save rendered the image to the output path (used only when animation is disabled)");
1120 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1121 prop = RNA_def_boolean(ot->srna, "view_context", 1, "View Context", "Use the current 3D view for rendering, else use scene settings");
1122 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1126 /* function for getting an opengl buffer from a View3D, used by sequencer */
1127 // extern void *sequencer_view3d_cb;