Ejemplo n.º 1
0
static void task_free(TaskPool *pool, Task *task, const int thread_id)
{
	task_data_free(task, thread_id);
	BLI_assert(thread_id >= 0);
	BLI_assert(thread_id <= pool->scheduler->num_threads);
	if (thread_id == 0) {
		BLI_assert(pool->use_local_tls || BLI_thread_is_main());
	}
	TaskThreadLocalStorage *tls = get_task_tls(pool, thread_id);
	TaskMemPool *task_mempool = &tls->task_mempool;
	if (task_mempool->num_tasks < MEMPOOL_SIZE - 1) {
		/* Successfully allowed the task to be re-used later. */
		task_mempool->tasks[task_mempool->num_tasks] = task;
		++task_mempool->num_tasks;
	}
	else {
		/* Local storage saturated, no other way than just discard
		 * the memory.
		 *
		 * TODO(sergey): We can perhaps store such pointer in a global
		 * scheduler pool, maybe it'll be faster than discarding and
		 * allocating again.
		 */
		MEM_freeN(task);
#ifdef DEBUG_STATS
		pool->mempool_stats[thread_id].num_discard++;
#endif
	}
}
Ejemplo n.º 2
0
void ED_render_id_flush_update(Main *bmain, ID *id)
{
	/* this can be called from render or baking thread when a python script makes
	 * changes, in that case we don't want to do any editor updates, and making
	 * GPU changes is not possible because OpenGL only works in the main thread */
	if (!BLI_thread_is_main())
		return;

	switch (GS(id->name)) {
		case ID_MA:
			material_changed(bmain, (Material *)id);
			render_engine_flag_changed(bmain, RE_ENGINE_UPDATE_MA);
			break;
		case ID_TE:
			texture_changed(bmain, (Tex *)id);
			break;
		case ID_WO:
			world_changed(bmain, (World *)id);
			break;
		case ID_LA:
			lamp_changed(bmain, (Lamp *)id);
			break;
		case ID_IM:
			image_changed(bmain, (Image *)id);
			break;
		case ID_SCE:
			scene_changed(bmain, (Scene *)id);
			render_engine_flag_changed(bmain, RE_ENGINE_UPDATE_OTHER);
			break;
		default:
			render_engine_flag_changed(bmain, RE_ENGINE_UPDATE_OTHER);
			break;
	}
	
}
Ejemplo n.º 3
0
/* MUST run on the main thread. */
void *EEVEE_lightbake_job_data_alloc(struct Main *bmain,
                                     struct ViewLayer *view_layer,
                                     struct Scene *scene,
                                     bool run_as_job,
                                     int frame)
{
  BLI_assert(BLI_thread_is_main());

  EEVEE_LightBake *lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake");

  lbake->depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER);
  lbake->scene = scene;
  lbake->bmain = bmain;
  lbake->view_layer_input = view_layer;
  lbake->own_resources = true;
  lbake->own_light_cache = false;
  lbake->mutex = BLI_mutex_alloc();
  lbake->frame = frame;

  if (run_as_job) {
    lbake->gl_context = WM_opengl_context_create();
    wm_window_reset_drawable();
  }

  return lbake;
}
Ejemplo n.º 4
0
BLI_INLINE TaskThreadLocalStorage *get_task_tls(TaskPool *pool,
                                                const int thread_id)
{
	TaskScheduler *scheduler = pool->scheduler;
	BLI_assert(thread_id >= 0);
	BLI_assert(thread_id <= scheduler->num_threads);
	if (pool->use_local_tls && thread_id == 0) {
		BLI_assert(pool->thread_id == 0);
		BLI_assert(!BLI_thread_is_main());
		BLI_assert(pthread_equal(pthread_self(), pool->creator_thread_id));
		return &pool->local_tls;
	}
	if (thread_id == 0) {
		BLI_assert(BLI_thread_is_main());
		return &scheduler->task_threads[pool->thread_id].tls;
	}
	return &scheduler->task_threads[thread_id].tls;
}
Ejemplo n.º 5
0
static TaskPool *task_pool_create_ex(TaskScheduler *scheduler, void *userdata, const bool is_background)
{
	TaskPool *pool = MEM_mallocN(sizeof(TaskPool), "TaskPool");

#ifndef NDEBUG
	/* Assert we do not try to create a background pool from some parent task - those only work OK from main thread. */
	if (is_background) {
		const pthread_t thread_id = pthread_self();
		int i = scheduler->num_threads;

		while (i--) {
			BLI_assert(!pthread_equal(scheduler->threads[i], thread_id));
		}
	}
#endif

	pool->scheduler = scheduler;
	pool->num = 0;
	pool->done = 0;
	pool->num_threads = 0;
	pool->currently_running_tasks = 0;
	pool->do_cancel = false;
	pool->run_in_background = is_background;

	BLI_mutex_init(&pool->num_mutex);
	BLI_condition_init(&pool->num_cond);

	pool->userdata = userdata;
	BLI_mutex_init(&pool->user_mutex);

	if (BLI_thread_is_main()) {
		pool->task_mempool = scheduler->task_mempool;
	}
	else {
		pool->task_mempool = &pool->task_mempool_local;
		pool->task_mempool_local.num_tasks = 0;
	}

#ifdef DEBUG_STATS
	pool->mempool_stats =
	        MEM_callocN(sizeof(*pool->mempool_stats) * (scheduler->num_threads + 1),
	                    "per-taskpool mempool stats");
#endif

	/* Ensure malloc will go fine from threads,
	 *
	 * This is needed because we could be in main thread here
	 * and malloc could be non-threda safe at this point because
	 * no other jobs are running.
	 */
	BLI_begin_threaded_malloc();

	return pool;
}
Ejemplo n.º 6
0
/* use for updating while a python script runs - in case of file load */
void BPY_context_update(bContext *C)
{
	/* don't do this from a non-main (e.g. render) thread, it can cause a race
	 * condition on C->data.recursion. ideal solution would be to disable
	 * context entirely from non-main threads, but that's more complicated */
	if (!BLI_thread_is_main())
		return;

	BPy_SetContext(C);
	bpy_import_main_set(CTX_data_main(C));
	BPY_modules_update(C); /* can give really bad results if this isn't here */
}
Ejemplo n.º 7
0
void ED_preview_ensure_dbase(void)
{
#ifndef WITH_HEADLESS
	static bool base_initialized = false;
	BLI_assert(BLI_thread_is_main());
	if (!base_initialized) {
		G_pr_main = load_main_from_memory(datatoc_preview_blend, datatoc_preview_blend_size);
		G_pr_main_cycles = load_main_from_memory(datatoc_preview_cycles_blend, datatoc_preview_cycles_blend_size);
		base_initialized = true;
	}
#endif
}
Ejemplo n.º 8
0
void ED_render_scene_update(Main *bmain, Scene *scene, int updated)
{
	/* viewport rendering update on data changes, happens after depsgraph
	 * updates if there was any change. context is set to the 3d view */
	bContext *C;
	bScreen *sc;
	ScrArea *sa;
	ARegion *ar;

	/* don't do this render engine update if we're updating the scene from
	 * other threads doing e.g. rendering or baking jobs */
	if (!BLI_thread_is_main())
		return;

	C = CTX_create();
	CTX_data_main_set(C, bmain);
	CTX_data_scene_set(C, scene);

	CTX_wm_manager_set(C, bmain->wm.first);

	for (sc = bmain->screen.first; sc; sc = sc->id.next) {
		for (sa = sc->areabase.first; sa; sa = sa->next) {
			if (sa->spacetype != SPACE_VIEW3D)
				continue;

			for (ar = sa->regionbase.first; ar; ar = ar->next) {
				RegionView3D *rv3d;
				RenderEngine *engine;

				if (ar->regiontype != RGN_TYPE_WINDOW)
					continue;

				rv3d = ar->regiondata;
				engine = rv3d->render_engine;

				if (engine && (updated || (engine->flag & RE_ENGINE_DO_UPDATE))) {
					CTX_wm_screen_set(C, sc);
					CTX_wm_area_set(C, sa);
					CTX_wm_region_set(C, ar);

					engine->flag &= ~RE_ENGINE_DO_UPDATE;
					engine->type->view_update(engine, C);
				}
			}
		}
	}

	CTX_free(C);
}
Ejemplo n.º 9
0
void wm_window_process_events(const bContext *C)
{
    int hasevent;

    BLI_assert(BLI_thread_is_main());

    hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */

    if (hasevent)
        GHOST_DispatchEvents(g_system);

    hasevent |= wm_window_timer(C);

    /* no event, we sleep 5 milliseconds */
    if (hasevent == 0)
        PIL_sleep_ms(5);
}
Ejemplo n.º 10
0
/* exported as handle callback to bke blender.c */
void wm_window_testbreak(void)
{
    static double ltime = 0;
    double curtime = PIL_check_seconds_timer();

    BLI_assert(BLI_thread_is_main());

    /* only check for breaks every 50 milliseconds
     * if we get called more often.
     */
    if ((curtime - ltime) > 0.05) {
        int hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */

        if (hasevent)
            GHOST_DispatchEvents(g_system);

        ltime = curtime;
    }
}
Ejemplo n.º 11
0
/* release a GPUBuffer; does not free the actual buffer or its data,
   but rather moves it to the pool of recently-free'd buffers for
   possible re-use*/
void GPU_buffer_free(GPUBuffer *buffer)
{
	GPUBufferPool *pool;
	int i;

	if(!buffer)
		return;

	pool = gpu_get_global_buffer_pool();

	/* free the last used buffer in the queue if no more space, but only
	   if we are in the main thread. for e.g. rendering or baking it can
	   happen that we are in other thread and can't call OpenGL, in that
	   case cleanup will be done GPU_buffer_pool_free_unused */
	if(BLI_thread_is_main()) {
		/* in main thread, safe to decrease size of pool back
		   down to MAX_FREE_GPU_BUFFERS */
		while(pool->totbuf >= MAX_FREE_GPU_BUFFERS)
			gpu_buffer_pool_delete_last(pool);
	}
	else {
		/* outside of main thread, can't safely delete the
		   buffer, so increase pool size */
		if(pool->maxsize == pool->totbuf) {
			pool->maxsize += MAX_FREE_GPU_BUFFERS;
			pool->buffers = MEM_reallocN(pool->buffers,
						     sizeof(GPUBuffer*) * pool->maxsize);
		}
	}

	/* shift pool entries up by one */
	for(i = pool->totbuf; i > 0; i--)
		pool->buffers[i] = pool->buffers[i-1];

	/* insert the buffer into the beginning of the pool */
	pool->buffers[0] = buffer;
	pool->totbuf++;
}
Ejemplo n.º 12
0
/**
 * \see #wm_homefile_write_exec wraps #BLO_write_file in a similar way.
 */
int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports)
{
	Library *li;
	int len;
	int ret = -1;
	BlendThumbnail *thumb, *main_thumb;
	ImBuf *ibuf_thumb = NULL;

	len = strlen(filepath);
	
	if (len == 0) {
		BKE_report(reports, RPT_ERROR, "Path is empty, cannot save");
		return ret;
	}

	if (len >= FILE_MAX) {
		BKE_report(reports, RPT_ERROR, "Path too long, cannot save");
		return ret;
	}
	
	/* Check if file write permission is ok */
	if (BLI_exists(filepath) && !BLI_file_is_writable(filepath)) {
		BKE_reportf(reports, RPT_ERROR, "Cannot save blend file, path '%s' is not writable", filepath);
		return ret;
	}
 
	/* note: used to replace the file extension (to ensure '.blend'),
	 * no need to now because the operator ensures,
	 * its handy for scripts to save to a predefined name without blender editing it */
	
	/* send the OnSave event */
	for (li = G.main->library.first; li; li = li->id.next) {
		if (BLI_path_cmp(li->filepath, filepath) == 0) {
			BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
			return ret;
		}
	}

	/* Call pre-save callbacks befores writing preview, that way you can generate custom file thumbnail... */
	BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE);

	/* blend file thumbnail */
	/* save before exit_editmode, otherwise derivedmeshes for shared data corrupt #27765) */
	/* Main now can store a .blend thumbnail, usefull for background mode or thumbnail customization. */
	main_thumb = thumb = CTX_data_main(C)->blen_thumb;
	if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) {
		ibuf_thumb = blend_file_thumb(CTX_data_scene(C), CTX_wm_screen(C), &thumb);
	}

	/* operator now handles overwrite checks */

	if (G.fileflags & G_AUTOPACK) {
		packAll(G.main, reports, false);
	}

	/* don't forget not to return without! */
	WM_cursor_wait(1);
	
	ED_editors_flush_edits(C, false);

	fileflags |= G_FILE_HISTORY; /* write file history */

	/* first time saving */
	/* XXX temp solution to solve bug, real fix coming (ton) */
	if ((G.main->name[0] == '\0') && !(fileflags & G_FILE_SAVE_COPY)) {
		BLI_strncpy(G.main->name, filepath, sizeof(G.main->name));
	}

	/* XXX temp solution to solve bug, real fix coming (ton) */
	G.main->recovered = 0;
	
	if (BLO_write_file(CTX_data_main(C), filepath, fileflags, reports, thumb)) {
		const bool do_history = (G.background == false) && (CTX_wm_manager(C)->op_undo_depth == 0);

		if (!(fileflags & G_FILE_SAVE_COPY)) {
			G.relbase_valid = 1;
			BLI_strncpy(G.main->name, filepath, sizeof(G.main->name));  /* is guaranteed current file */
	
			G.save_over = 1; /* disable untitled.blend convention */
		}

		BKE_BIT_TEST_SET(G.fileflags, fileflags & G_FILE_COMPRESS, G_FILE_COMPRESS);
		BKE_BIT_TEST_SET(G.fileflags, fileflags & G_FILE_AUTOPLAY, G_FILE_AUTOPLAY);

		/* prevent background mode scripts from clobbering history */
		if (do_history) {
			wm_history_file_update();
		}

		BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST);

		/* run this function after because the file cant be written before the blend is */
		if (ibuf_thumb) {
			IMB_thumb_delete(filepath, THB_FAIL); /* without this a failed thumb overrides */
			ibuf_thumb = IMB_thumb_create(filepath, THB_LARGE, THB_SOURCE_BLEND, ibuf_thumb);
		}

		ret = 0;  /* Success. */
	}

	if (ibuf_thumb) {
		IMB_freeImBuf(ibuf_thumb);
	}
	if (thumb && thumb != main_thumb) {
		MEM_freeN(thumb);
	}

	WM_cursor_wait(0);

	return ret;
}
Ejemplo n.º 13
0
int wm_file_write(bContext *C, const char *filepath, int fileflags, ReportList *reports)
{
	Library *li;
	int len;
	int *thumb = NULL;
	ImBuf *ibuf_thumb = NULL;

	len = strlen(filepath);
	
	if (len == 0) {
		BKE_report(reports, RPT_ERROR, "Path is empty, cannot save");
		return -1;
	}

	if (len >= FILE_MAX) {
		BKE_report(reports, RPT_ERROR, "Path too long, cannot save");
		return -1;
	}
 
	/* note: used to replace the file extension (to ensure '.blend'),
	 * no need to now because the operator ensures,
	 * its handy for scripts to save to a predefined name without blender editing it */
	
	/* send the OnSave event */
	for (li = G.main->library.first; li; li = li->id.next) {
		if (BLI_path_cmp(li->filepath, filepath) == 0) {
			BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
			return -1;
		}
	}

	/* blend file thumbnail */
	/* save before exit_editmode, otherwise derivedmeshes for shared data corrupt #27765) */
	if ((U.flag & USER_SAVE_PREVIEWS) && BLI_thread_is_main()) {
		ibuf_thumb = blend_file_thumb(CTX_data_scene(C), CTX_wm_screen(C), &thumb);
	}

	BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_PRE);

	/* operator now handles overwrite checks */

	if (G.fileflags & G_AUTOPACK) {
		packAll(G.main, reports);
	}

	ED_object_editmode_load(CTX_data_edit_object(C));
	ED_sculpt_force_update(C);

	/* don't forget not to return without! */
	WM_cursor_wait(1);
	
	fileflags |= G_FILE_HISTORY; /* write file history */

	/* first time saving */
	/* XXX temp solution to solve bug, real fix coming (ton) */
	if ((G.main->name[0] == '\0') && !(fileflags & G_FILE_SAVE_COPY)) {
		BLI_strncpy(G.main->name, filepath, sizeof(G.main->name));
	}

	/* XXX temp solution to solve bug, real fix coming (ton) */
	G.main->recovered = 0;
	
	if (BLO_write_file(CTX_data_main(C), filepath, fileflags, reports, thumb)) {
		if (!(fileflags & G_FILE_SAVE_COPY)) {
			G.relbase_valid = 1;
			BLI_strncpy(G.main->name, filepath, sizeof(G.main->name));  /* is guaranteed current file */
	
			G.save_over = 1; /* disable untitled.blend convention */
		}

		if (fileflags & G_FILE_COMPRESS) G.fileflags |= G_FILE_COMPRESS;
		else G.fileflags &= ~G_FILE_COMPRESS;
		
		if (fileflags & G_FILE_AUTOPLAY) G.fileflags |= G_FILE_AUTOPLAY;
		else G.fileflags &= ~G_FILE_AUTOPLAY;

		/* prevent background mode scripts from clobbering history */
		if (!G.background) {
			write_history();
		}

		BLI_callback_exec(G.main, NULL, BLI_CB_EVT_SAVE_POST);

		/* run this function after because the file cant be written before the blend is */
		if (ibuf_thumb) {
			IMB_thumb_delete(filepath, THB_FAIL); /* without this a failed thumb overrides */
			ibuf_thumb = IMB_thumb_create(filepath, THB_NORMAL, THB_SOURCE_BLEND, ibuf_thumb);
			IMB_freeImBuf(ibuf_thumb);
		}

		if (thumb) MEM_freeN(thumb);
	}
	else {
		if (ibuf_thumb) IMB_freeImBuf(ibuf_thumb);
		if (thumb) MEM_freeN(thumb);
		
		WM_cursor_wait(0);
		return -1;
	}

	WM_cursor_wait(0);
	
	return 0;
}
Ejemplo n.º 14
0
void ED_render_scene_update(Main *bmain, Scene *scene, int updated)
{
	/* viewport rendering update on data changes, happens after depsgraph
	 * updates if there was any change. context is set to the 3d view */
	bContext *C;
	wmWindowManager *wm;
	wmWindow *win;
	static bool recursive_check = false;

	/* don't do this render engine update if we're updating the scene from
	 * other threads doing e.g. rendering or baking jobs */
	if (!BLI_thread_is_main())
		return;

	/* don't call this recursively for frame updates */
	if (recursive_check)
		return;

	/* Do not call if no WM available, see T42688. */
	if (BLI_listbase_is_empty(&bmain->wm))
		return;

	recursive_check = true;

	C = CTX_create();
	CTX_data_main_set(C, bmain);
	CTX_data_scene_set(C, scene);

	CTX_wm_manager_set(C, bmain->wm.first);
	wm = bmain->wm.first;
	
	for (win = wm->windows.first; win; win = win->next) {
		bScreen *sc = win->screen;
		ScrArea *sa;
		ARegion *ar;
		
		CTX_wm_window_set(C, win);
		
		for (sa = sc->areabase.first; sa; sa = sa->next) {
			if (sa->spacetype != SPACE_VIEW3D)
				continue;

			for (ar = sa->regionbase.first; ar; ar = ar->next) {
				RegionView3D *rv3d;
				RenderEngine *engine;

				if (ar->regiontype != RGN_TYPE_WINDOW)
					continue;

				rv3d = ar->regiondata;
				engine = rv3d->render_engine;

				/* call update if the scene changed, or if the render engine
				 * tagged itself for update (e.g. because it was busy at the
				 * time of the last update) */
				if (engine && (updated || (engine->flag & RE_ENGINE_DO_UPDATE))) {

					CTX_wm_screen_set(C, sc);
					CTX_wm_area_set(C, sa);
					CTX_wm_region_set(C, ar);

					engine->flag &= ~RE_ENGINE_DO_UPDATE;
					engine->type->view_update(engine, C);
				}
			}
		}
	}

	CTX_free(C);

	recursive_check = false;
}