Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_callbacks.h3
-rw-r--r--source/blender/editors/object/object_bake_api.c16
-rw-r--r--source/blender/makesrna/RNA_enum_items.h1
-rw-r--r--source/blender/makesrna/intern/rna_wm.c17
-rw-r--r--source/blender/makesrna/intern/rna_wm_api.c1
-rw-r--r--source/blender/python/intern/bpy_app.c54
-rw-r--r--source/blender/python/intern/bpy_app_handlers.c4
-rw-r--r--source/blender/windowmanager/WM_api.h9
-rw-r--r--source/blender/windowmanager/intern/wm_jobs.c61
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;
+}