diff options
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_callbacks.h | 3 | ||||
-rw-r--r-- | source/blender/editors/object/object_bake_api.c | 16 | ||||
-rw-r--r-- | source/blender/makesrna/RNA_enum_items.h | 1 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_wm.c | 17 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_wm_api.c | 1 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_app.c | 54 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_app_handlers.c | 4 | ||||
-rw-r--r-- | source/blender/windowmanager/WM_api.h | 9 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_jobs.c | 61 |
9 files changed, 158 insertions, 8 deletions
diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h index 8b2af96a063..d8c67c52edc 100644 --- a/source/blender/blenkernel/BKE_callbacks.h +++ b/source/blender/blenkernel/BKE_callbacks.h @@ -97,6 +97,9 @@ typedef enum { BKE_CB_EVT_XR_SESSION_START_PRE, BKE_CB_EVT_ANNOTATION_PRE, BKE_CB_EVT_ANNOTATION_POST, + BKE_CB_EVT_OBJECT_BAKE_PRE, + BKE_CB_EVT_OBJECT_BAKE_COMPLETE, + BKE_CB_EVT_OBJECT_BAKE_CANCEL, BKE_CB_EVT_TOT, } eCbEvent; diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 114b2ce8102..4c1e90255f6 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -21,6 +21,7 @@ #include "BLI_path_util.h" #include "BLI_string.h" +#include "BKE_callbacks.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" @@ -1806,6 +1807,17 @@ static void bake_startjob(void *bkv, short *UNUSED(stop), short *do_update, floa RE_SetReports(bkr->render, NULL); } +static void bake_job_complete(void *bkv) +{ + BakeAPIRender *bkr = (BakeAPIRender *)bkv; + BKE_callback_exec_id(bkr->main, &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_COMPLETE); +} +static void bake_job_canceled(void *bkv) +{ + BakeAPIRender *bkr = (BakeAPIRender *)bkv; + BKE_callback_exec_id(bkr->main, &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_CANCEL); +} + static void bake_freejob(void *bkv) { BakeAPIRender *bkr = (BakeAPIRender *)bkv; @@ -1941,6 +1953,7 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) /* init bake render */ bake_init_api_data(op, C, bkr); + BKE_callback_exec_id(CTX_data_main(C), &bkr->ob->id, BKE_CB_EVT_OBJECT_BAKE_PRE); re = bkr->render; /* setup new render */ @@ -1958,7 +1971,8 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) /* TODO: only draw bake image, can we enforce this. */ WM_jobs_timer( wm_job, 0.5, (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) ? NC_GEOM | ND_DATA : NC_IMAGE, 0); - WM_jobs_callbacks(wm_job, bake_startjob, NULL, NULL, NULL); + WM_jobs_callbacks_ex( + wm_job, bake_startjob, NULL, NULL, NULL, bake_job_complete, bake_job_canceled); G.is_break = false; G.is_rendering = true; diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index 37af598729b..fd264932363 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -147,6 +147,7 @@ DEF_ENUM(rna_enum_keymap_propvalue_items) DEF_ENUM(rna_enum_operator_context_items) DEF_ENUM(rna_enum_wm_report_items) +DEF_ENUM(rna_enum_wm_job_type_items) DEF_ENUM(rna_enum_property_type_items) DEF_ENUM(rna_enum_property_subtype_items) diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 1dc2cbe9e69..92be6310716 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -24,6 +24,7 @@ #include "rna_internal.h" +#include "WM_api.h" #include "WM_types.h" #ifdef RNA_RUNTIME @@ -123,6 +124,22 @@ static const EnumPropertyItem event_ndof_type_items[] = { }; #endif /* RNA_RUNTIME */ +/** + * Job types for use in the `bpy.app.is_job_running(job_type)` call. + * + * This is a subset of the `WM_JOB_TYPE_...` anonymous enum defined in `WM_api.h`. It is + * intentionally kept as a subset, such that by default how jobs are handled is kept as an + * "internal implementation detail" of Blender, rather than a public, reliable part of the API. + * + * This array can be expanded on a case-by-case basis, when there is a clear and testable use case. + */ +const EnumPropertyItem rna_enum_wm_job_type_items[] = { + {WM_JOB_TYPE_RENDER, "RENDER", 0, "Regular rendering", ""}, + {WM_JOB_TYPE_RENDER_PREVIEW, "RENDER_PREVIEW", 0, "Rendering previews", ""}, + {WM_JOB_TYPE_OBJECT_BAKE, "OBJECT_BAKE", 0, "Object Baking", ""}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_event_type_items[] = { /* - Note we abuse 'tooltip' message here to store a 'compact' form of some (too) long names. * - Intentionally excluded: #CAPSLOCKKEY, #UNKNOWNKEY. diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 5da65510399..b9f36d35ee8 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -23,6 +23,7 @@ #include "wm_cursors.h" #include "wm_event_types.h" +#include "WM_api.h" #include "WM_types.h" #include "rna_internal.h" /* own include */ diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 621cc79a8db..f54bf3e2774 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -36,15 +36,19 @@ #include "BKE_appdir.h" #include "BKE_blender_version.h" #include "BKE_global.h" +#include "BKE_main.h" #include "DNA_ID.h" #include "UI_interface_icons.h" +#include "RNA_enum_types.h" /* For `rna_enum_wm_job_type_items`. */ + /* for notifiers */ #include "WM_api.h" #include "WM_types.h" +#include "../generic/py_capi_rna.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" @@ -450,6 +454,44 @@ static PyGetSetDef bpy_app_getsets[] = { {NULL, NULL, NULL, NULL, NULL}, }; +PyDoc_STRVAR(bpy_app_is_job_running_doc, + ".. staticmethod:: is_job_running(job_type)\n" + "\n" + " Check whether a job of the given type is running.\n" + "\n" + " :arg job_type: job type in ['RENDER', 'RENDER_PREVIEW', OBJECT_BAKE]." + " :type job_type: str\n" + " :return: Whether a job of the given type is currently running.\n" + " :rtype: bool.\n"); +static PyObject *bpy_app_is_job_running(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) +{ + struct BPy_EnumProperty_Parse job_type_enum = { + .items = rna_enum_wm_job_type_items, + .value = 0, + }; + static const char *_keywords[] = {"job_type", NULL}; + static _PyArg_Parser _parser = { + "O&" /* `job_type` */ + ":is_job_running", + _keywords, + 0, + }; + if (!_PyArg_ParseTupleAndKeywordsFast( + args, kwds, &_parser, pyrna_enum_value_parse_string, &job_type_enum)) { + return NULL; + } + wmWindowManager *wm = G_MAIN->wm.first; + return PyBool_FromLong(WM_jobs_has_running_type(wm, job_type_enum.value)); +} + +static struct PyMethodDef bpy_app_methods[] = { + {"is_job_running", + (PyCFunction)bpy_app_is_job_running, + METH_VARARGS | METH_KEYWORDS | METH_STATIC, + bpy_app_is_job_running_doc}, + {NULL, NULL, 0, NULL}, +}; + static void py_struct_seq_getset_init(void) { /* tricky dynamic members, not to py-spec! */ @@ -459,6 +501,17 @@ static void py_struct_seq_getset_init(void) Py_DECREF(item); } } + +static void py_struct_seq_method_init(void) +{ + for (PyMethodDef *method = bpy_app_methods; method->ml_name; method++) { + BLI_assert_msg(method->ml_flags & METH_STATIC, "Only static methods make sense for 'bpy.app'"); + PyObject *item = PyCFunction_New(method, NULL); + PyDict_SetItemString(BlenderAppType.tp_dict, method->ml_name, item); + Py_DECREF(item); + } +} + /* end dynamic bpy.app */ PyObject *BPY_app_struct(void) @@ -477,6 +530,7 @@ PyObject *BPY_app_struct(void) /* kindof a hack ontop of PyStructSequence */ py_struct_seq_getset_init(); + py_struct_seq_method_init(); return ret; } diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index bf427d9639a..40f4b8af1cc 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -67,6 +67,10 @@ static PyStructSequence_Field app_cb_info_fields[] = { {"annotation_pre", "on drawing an annotation (before)"}, {"annotation_post", "on drawing an annotation (after)"}, + {"object_bake_pre", "before starting a bake job"}, + {"object_bake_complete", "on completing a bake job; will be called in the main thread"}, + {"object_bake_cancel", "on canceling a bake job; will be called in the main thread"}, + /* sets the permanent tag */ #define APP_CB_OTHER_FIELDS 1 {"persistent", diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 890872a06bc..dec1260ad1d 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1400,6 +1400,14 @@ void WM_jobs_callbacks(struct wmJob *, void (*update)(void *), void (*endjob)(void *)); +void WM_jobs_callbacks_ex(wmJob *wm_job, + wm_jobs_start_callback startjob, + void (*initjob)(void *), + void (*update)(void *), + void (*endjob)(void *), + void (*completed)(void *), + void (*canceled)(void *)); + /** * If job running, the same owner gave it a new job. * if different owner starts existing startjob, it suspends itself @@ -1426,6 +1434,7 @@ void WM_jobs_kill_all_except(struct wmWindowManager *wm, const void *owner); void WM_jobs_kill_type(struct wmWindowManager *wm, const void *owner, int job_type); bool WM_jobs_has_running(const struct wmWindowManager *wm); +bool WM_jobs_has_running_type(const struct wmWindowManager *wm, int job_type); void WM_job_main_thread_lock_acquire(struct wmJob *job); void WM_job_main_thread_lock_release(struct wmJob *job); diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c index b44cf9e48b8..bc80f56ee13 100644 --- a/source/blender/windowmanager/intern/wm_jobs.c +++ b/source/blender/windowmanager/intern/wm_jobs.c @@ -87,6 +87,16 @@ struct wmJob { * Executed in main thread. */ void (*endjob)(void *); + /** + * Called when job is stopped normally, i.e. by simply completing the startjob function. + * Executed in main thread. + */ + void (*completed)(void *); + /** + * Called when job is stopped abnormally, i.e. when stop=true but ready=false. + * Executed in main thread. + */ + void (*canceled)(void *); /** Running jobs each have own timer */ double timestep; @@ -344,10 +354,23 @@ void WM_jobs_callbacks(wmJob *wm_job, void (*update)(void *), void (*endjob)(void *)) { + WM_jobs_callbacks_ex(wm_job, startjob, initjob, update, endjob, NULL, NULL); +} + +void WM_jobs_callbacks_ex(wmJob *wm_job, + wm_jobs_start_callback startjob, + void (*initjob)(void *), + void (*update)(void *), + void (*endjob)(void *), + void (*completed)(void *), + void (*canceled)(void *)) +{ wm_job->startjob = startjob; wm_job->initjob = initjob; wm_job->update = update; wm_job->endjob = endjob; + wm_job->completed = completed; + wm_job->canceled = canceled; } static void *do_job_thread(void *job_v) @@ -465,6 +488,25 @@ void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job) } } +static void wm_job_end(wmJob *wm_job) +{ + BLI_assert_msg(BLI_thread_is_main(), "wm_job_end should only be called from the main thread"); + if (wm_job->endjob) { + wm_job->endjob(wm_job->run_customdata); + } + + /* Do the final callback based on whether the job was run to completion or not. + * Not all jobs have the same way of signalling cancellation (f.e. rendering + * stops when G.is_break=true, but doesn't set any wm_job properties to cancel + * the WM job). */ + const bool was_canceled = wm_job->stop || G.is_break; + void (*final_callback)(void *) = (wm_job->ready && !was_canceled) ? wm_job->completed : + wm_job->canceled; + if (final_callback) { + final_callback(wm_job->run_customdata); + } +} + static void wm_job_free(wmWindowManager *wm, wmJob *wm_job) { BLI_remlink(&wm->jobs, wm_job); @@ -485,10 +527,7 @@ static void wm_jobs_kill_job(wmWindowManager *wm, wmJob *wm_job) WM_job_main_thread_lock_release(wm_job); BLI_threadpool_end(&wm_job->threads); WM_job_main_thread_lock_acquire(wm_job); - - if (wm_job->endjob) { - wm_job->endjob(wm_job->run_customdata); - } + wm_job_end(wm_job); } if (wm_job->wt) { @@ -600,9 +639,7 @@ void wm_jobs_timer(wmWindowManager *wm, wmTimer *wt) } if (wm_job->ready) { - if (wm_job->endjob) { - wm_job->endjob(wm_job->run_customdata); - } + wm_job_end(wm_job); /* free own data */ wm_job->run_free(wm_job->run_customdata); @@ -670,3 +707,13 @@ bool WM_jobs_has_running(const wmWindowManager *wm) return false; } + +bool WM_jobs_has_running_type(const struct wmWindowManager *wm, int job_type) +{ + LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) { + if (wm_job->running && wm_job->job_type == job_type) { + return true; + } + } + return false; +} |