Blender V4.5
object_bake.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstring>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_mesh_types.h"
14#include "DNA_object_types.h"
15#include "DNA_scene_types.h"
16#include "DNA_screen_types.h"
17
18#include "BLI_listbase.h"
19#include "BLI_utildefines.h"
20
21#include "BKE_attribute.hh"
22#include "BKE_context.hh"
23#include "BKE_customdata.hh"
24#include "BKE_global.hh"
25#include "BKE_image.hh"
27#include "BKE_modifier.hh"
28#include "BKE_multires.hh"
29#include "BKE_report.hh"
30#include "BKE_scene.hh"
31
32#include "RE_multires_bake.h"
33#include "RE_pipeline.h"
34
35#include "IMB_imbuf.hh"
36#include "IMB_imbuf_types.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "ED_screen.hh"
42#include "ED_uvedit.hh"
43
44#include "object_intern.hh"
45
46namespace blender::ed::object {
47
48static Image *bake_object_image_get(Object *ob, int mat_nr)
49{
50 Image *image = nullptr;
51 ED_object_get_active_image(ob, mat_nr + 1, &image, nullptr, nullptr, nullptr);
52 return image;
53}
54
56{
57 Image **image_array = MEM_malloc_arrayN<Image *>(ob->totcol, __func__);
58 for (int i = 0; i < ob->totcol; i++) {
59 image_array[i] = bake_object_image_get(ob, i);
60 }
61 return image_array;
62}
63
64/* ****************** multires BAKING ********************** */
65
66/* holder of per-object data needed for bake job
67 * needed to make job totally thread-safe */
70 /* material aligned image array (for per-face bake image) */
71 struct {
73 int len;
78};
79
80/* data passing to multires-baker job */
103
105{
106 Scene *scene = CTX_data_scene(C);
107 Object *ob;
108 Mesh *mesh;
110 bool ok = true;
111 int a;
112
113 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
114 ob = base->object;
115
116 if (ob->type != OB_MESH) {
118 op->reports, RPT_ERROR, "Baking of multires data only works with an active mesh object");
119
120 ok = false;
121 break;
122 }
123
124 mesh = (Mesh *)ob->data;
125 mmd = get_multires_modifier(scene, ob, false);
126
127 /* Multi-resolution should be and be last in the stack */
128 if (ok && mmd) {
129 ModifierData *md;
130
131 ok = mmd->totlvl > 0;
132
133 for (md = (ModifierData *)mmd->modifier.next; md && ok; md = md->next) {
135 ok = false;
136 }
137 }
138 }
139 else {
140 ok = false;
141 }
142
143 if (!ok) {
144 BKE_report(op->reports, RPT_ERROR, "Multires data baking requires multi-resolution object");
145
146 break;
147 }
148
149 if (!CustomData_has_layer(&mesh->corner_data, CD_PROP_FLOAT2)) {
150 BKE_report(op->reports, RPT_ERROR, "Mesh should be unwrapped before multires data baking");
151
152 ok = false;
153 }
154 else {
155 const bke::AttributeAccessor attributes = mesh->attributes();
156 const VArraySpan material_indices = *attributes.lookup<int>("material_index",
158 a = mesh->faces_num;
159 while (ok && a--) {
161 material_indices.is_empty() ? 0 : material_indices[a]);
162
163 if (!ima) {
165 op->reports, RPT_ERROR, "You should have active texture to use multires baker");
166
167 ok = false;
168 }
169 else {
171 ImageUser iuser;
172 BKE_imageuser_default(&iuser);
173 iuser.tile = tile->tile_number;
174
175 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
176
177 if (!ibuf) {
179 op->reports, RPT_ERROR, "Baking should happen to image with image buffer");
180
181 ok = false;
182 }
183 else {
184 if (ibuf->byte_buffer.data == nullptr && ibuf->float_buffer.data == nullptr) {
185 ok = false;
186 }
187
188 if (ibuf->float_buffer.data && !ELEM(ibuf->channels, 0, 4)) {
189 ok = false;
190 }
191
192 if (!ok) {
193 BKE_report(op->reports, RPT_ERROR, "Baking to unsupported image type");
194 }
195 }
196
197 BKE_image_release_ibuf(ima, ibuf, nullptr);
198 }
199 }
200 }
201 }
202
203 if (!ok) {
204 break;
205 }
206 }
208
209 return ok;
210}
211
213{
214 DerivedMesh *dm;
215 MultiresModifierData *mmd = get_multires_modifier(scene, ob, false);
216 Mesh *mesh = (Mesh *)ob->data;
217 MultiresModifierData tmp_mmd = dna::shallow_copy(*mmd);
218
219 *lvl = mmd->lvl;
220
221 if (mmd->lvl == 0) {
224 return cddm;
225 }
226
229 tmp_mmd.lvl = mmd->lvl;
230 tmp_mmd.sculptlvl = mmd->lvl;
232 cddm, &tmp_mmd, scene, ob, MultiresFlags::IgnoreSimplify);
233
234 cddm->release(cddm);
235
236 return dm;
237}
238
240{
241 Mesh *mesh = (Mesh *)ob->data;
242 MultiresModifierData *mmd = get_multires_modifier(scene, ob, false);
243 MultiresModifierData tmp_mmd = dna::shallow_copy(*mmd);
245 DerivedMesh *dm;
246
248
249 /* TODO: DM_set_only_copy wouldn't set mask for loop and poly data,
250 * but we really need BAREMESH only to save lots of memory
251 */
254
255 *lvl = mmd->totlvl;
256
257 tmp_mmd.lvl = mmd->totlvl;
258 tmp_mmd.sculptlvl = mmd->totlvl;
260 cddm, &tmp_mmd, scene, ob, MultiresFlags::IgnoreSimplify);
261 cddm->release(cddm);
262
263 return dm;
264}
265
270
272{
273 const float vec_alpha[4] = {0.0f, 0.0f, 0.0f, 0.0f};
274 const float vec_solid[4] = {0.0f, 0.0f, 0.0f, 1.0f};
275 const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f};
276 const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f};
277 const float disp_alpha[4] = {0.5f, 0.5f, 0.5f, 0.0f};
278 const float disp_solid[4] = {0.5f, 0.5f, 0.5f, 1.0f};
279
280 if ((image->id.tag & ID_TAG_DOIT) == 0) {
281 LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
282 ImageUser iuser;
283 BKE_imageuser_default(&iuser);
284 iuser.tile = tile->tile_number;
285
286 ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, nullptr);
287
288 if (flag == CLEAR_TANGENT_NORMAL) {
289 IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid);
290 }
291 else if (flag == CLEAR_DISPLACEMENT) {
292 IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? disp_alpha : disp_solid);
293 }
294 else {
295 IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid);
296 }
297
298 image->id.tag |= ID_TAG_DOIT;
299
300 BKE_image_release_ibuf(image, ibuf, nullptr);
301 }
302 }
303}
304
305static void clear_images_poly(Image **ob_image_array, int ob_image_array_len, ClearFlag flag)
306{
307 for (int i = 0; i < ob_image_array_len; i++) {
308 Image *image = ob_image_array[i];
309 if (image) {
310 image->id.tag &= ~ID_TAG_DOIT;
311 }
312 }
313
314 for (int i = 0; i < ob_image_array_len; i++) {
315 Image *image = ob_image_array[i];
316 if (image) {
317 clear_single_image(image, flag);
318 }
319 }
320
321 for (int i = 0; i < ob_image_array_len; i++) {
322 Image *image = ob_image_array[i];
323 if (image) {
324 image->id.tag &= ~ID_TAG_DOIT;
325 }
326 }
327}
328
330{
331 Object *ob;
332 Scene *scene = CTX_data_scene(C);
333 int objects_baked = 0;
334
335 if (!multiresbake_check(C, op)) {
336 return OPERATOR_CANCELLED;
337 }
338
339 if (scene->r.bake_flag & R_BAKE_CLEAR) { /* clear images */
340 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
341 ClearFlag clear_flag = ClearFlag(0);
342
343 ob = base->object;
344 // mesh = (Mesh *)ob->data;
345
346 if (scene->r.bake_mode == RE_BAKE_NORMALS) {
347 clear_flag = CLEAR_TANGENT_NORMAL;
348 }
349 else if (scene->r.bake_mode == RE_BAKE_DISPLACEMENT) {
350 clear_flag = CLEAR_DISPLACEMENT;
351 }
352
353 {
354 Image **ob_image_array = bake_object_image_get_array(ob);
355 clear_images_poly(ob_image_array, ob->totcol, clear_flag);
356 MEM_freeN(ob_image_array);
357 }
358 }
360 }
361
362 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
363 MultiresBakeRender bkr = {nullptr};
364
365 ob = base->object;
366
368
369 /* copy data stored in job descriptor */
370 bkr.scene = scene;
371 bkr.bake_margin = scene->r.bake_margin;
372 if (scene->r.bake_mode == RE_BAKE_NORMALS) {
374 }
375 else {
377 }
378 bkr.mode = scene->r.bake_mode;
380 bkr.bias = scene->r.bake_biasdist;
381 bkr.number_of_rays = scene->r.bake_samples;
382 bkr.threads = BKE_scene_num_threads(scene);
383 bkr.user_scale = (scene->r.bake_flag & R_BAKE_USERSCALE) ? scene->r.bake_user_scale : -1.0f;
384 // bkr.reports= op->reports;
385
386 /* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
388 bkr.ob_image.len = ob->totcol;
389
390 bkr.hires_dm = multiresbake_create_hiresdm(scene, ob, &bkr.tot_lvl);
391 bkr.lores_dm = multiresbake_create_loresdm(scene, ob, &bkr.lvl);
392
394
396
397 BLI_freelistN(&bkr.image);
398
399 bkr.lores_dm->release(bkr.lores_dm);
400 bkr.hires_dm->release(bkr.hires_dm);
401
402 objects_baked++;
403 }
405
406 if (!objects_baked) {
407 BKE_report(op->reports, RPT_ERROR, "No objects found to bake from");
408 }
409
410 return OPERATOR_FINISHED;
411}
412
417{
418 Scene *scene = CTX_data_scene(C);
419 Object *ob;
420
421 /* backup scene settings, so their changing in UI would take no effect on baker */
422 bkj->scene = scene;
423 bkj->bake_margin = scene->r.bake_margin;
424 if (scene->r.bake_mode == RE_BAKE_NORMALS) {
426 }
427 else {
428 bkj->bake_margin_type = scene->r.bake_margin_type;
429 }
430 bkj->mode = scene->r.bake_mode;
432 bkj->bake_clear = scene->r.bake_flag & R_BAKE_CLEAR;
433 bkj->bias = scene->r.bake_biasdist;
434 bkj->number_of_rays = scene->r.bake_samples;
435 bkj->threads = BKE_scene_num_threads(scene);
436 bkj->user_scale = (scene->r.bake_flag & R_BAKE_USERSCALE) ? scene->r.bake_user_scale : -1.0f;
437 // bkj->reports = op->reports;
438
439 CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
440 int lvl;
441
442 ob = base->object;
443
445
447
448 data->ob_image.array = bake_object_image_get_array(ob);
449 data->ob_image.len = ob->totcol;
450
451 /* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
452 data->hires_dm = multiresbake_create_hiresdm(scene, ob, &data->tot_lvl);
453 data->lores_dm = multiresbake_create_loresdm(scene, ob, &lvl);
454 data->lvl = lvl;
455
456 BLI_addtail(&bkj->data, data);
457 }
459}
460
461static void multiresbake_startjob(void *bkv, wmJobWorkerStatus *worker_status)
462{
463 MultiresBakeJob *bkj = static_cast<MultiresBakeJob *>(bkv);
464 int baked_objects = 0, tot_obj;
465
466 tot_obj = BLI_listbase_count(&bkj->data);
467
468 if (bkj->bake_clear) { /* clear images */
470 ClearFlag clear_flag = ClearFlag(0);
471
472 if (bkj->mode == RE_BAKE_NORMALS) {
473 clear_flag = CLEAR_TANGENT_NORMAL;
474 }
475 else if (bkj->mode == RE_BAKE_DISPLACEMENT) {
476 clear_flag = CLEAR_DISPLACEMENT;
477 }
478
479 clear_images_poly(data->ob_image.array, data->ob_image.len, clear_flag);
480 }
481 }
482
484 MultiresBakeRender bkr = {nullptr};
485
486 /* copy data stored in job descriptor */
487 bkr.scene = bkj->scene;
488 bkr.bake_margin = bkj->bake_margin;
490 bkr.mode = bkj->mode;
492 bkr.user_scale = bkj->user_scale;
493 // bkr.reports = bkj->reports;
494 bkr.ob_image.array = data->ob_image.array;
495 bkr.ob_image.len = data->ob_image.len;
496
497 /* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
498 bkr.lores_dm = data->lores_dm;
499 bkr.hires_dm = data->hires_dm;
500 bkr.tot_lvl = data->tot_lvl;
501 bkr.lvl = data->lvl;
502
503 /* needed for proper progress bar */
504 bkr.tot_obj = tot_obj;
505 bkr.baked_objects = baked_objects;
506
507 bkr.stop = &worker_status->stop;
508 bkr.do_update = &worker_status->do_update;
509 bkr.progress = &worker_status->progress;
510
511 bkr.bias = bkj->bias;
513 bkr.threads = bkj->threads;
514
516
517 data->images = bkr.image;
518
519 baked_objects++;
520 }
521}
522
523static void multiresbake_freejob(void *bkv)
524{
525 MultiresBakeJob *bkj = static_cast<MultiresBakeJob *>(bkv);
527
528 data = static_cast<MultiresBakerJobData *>(bkj->data.first);
529 while (data) {
530 next = data->next;
531 data->lores_dm->release(data->lores_dm);
532 data->hires_dm->release(data->hires_dm);
533
534 /* delete here, since this delete will be called from main thread */
535 LISTBASE_FOREACH (LinkData *, link, &data->images) {
536 Image *ima = (Image *)link->data;
538 }
539
540 MEM_freeN(data->ob_image.array);
541
542 BLI_freelistN(&data->images);
543
545 data = next;
546 }
547
548 MEM_freeN(bkj);
549}
550
552{
553 Scene *scene = CTX_data_scene(C);
554
555 if (!multiresbake_check(C, op)) {
556 return OPERATOR_CANCELLED;
557 }
558
561
562 if (!bkr->data.first) {
563 BKE_report(op->reports, RPT_ERROR, "No objects found to bake from");
564 MEM_freeN(bkr);
565 return OPERATOR_CANCELLED;
566 }
567
568 /* setup job */
571 scene,
572 "Multires Bake",
576 WM_jobs_timer(wm_job, 0.5, NC_IMAGE, 0); /* TODO: only draw bake image, can we enforce this. */
577 WM_jobs_callbacks(wm_job, multiresbake_startjob, nullptr, nullptr, nullptr);
578
579 G.is_break = false;
580
582 WM_cursor_wait(false);
583
584 /* add modal handler for ESC */
586
588}
589
590/* ****************** render BAKING ********************** */
591
594 wmOperator * /*op*/,
595 const wmEvent *event)
596{
597 /* no running blender, remove handler and pass through */
600 }
601
602 /* running render */
603 switch (event->type) {
604 case EVT_ESCKEY:
606 default: {
607 break;
608 }
609 }
611}
612
613static bool is_multires_bake(Scene *scene)
614{
616 return scene->r.bake_flag & R_BAKE_MULTIRES;
617 }
618
619 return false;
620}
621
623 wmOperator *op,
624 const wmEvent * /*event*/)
625{
626 Scene *scene = CTX_data_scene(C);
628
630
632
633 return result;
634}
635
637{
638 Scene *scene = CTX_data_scene(C);
640
641 if (!is_multires_bake(scene)) {
642 BLI_assert(0);
643 return result;
644 }
645
647
649
650 return result;
651}
652
654{
655 /* identifiers */
656 ot->name = "Bake";
657 ot->description = "Bake image textures of selected objects";
658 ot->idname = "OBJECT_OT_bake_image";
659
660 /* API callbacks. */
661 ot->exec = bake_image_exec;
665}
666
667} // namespace blender::ed::object
#define CTX_DATA_BEGIN(C, Type, instance, member)
wmWindow * CTX_wm_window(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
#define CTX_DATA_END
CustomData interface, see also DNA_customdata_types.h.
void CustomData_set_only_copy(const CustomData *data, eCustomDataMask mask)
const CustomData_MeshMasks CD_MASK_BAREMESH
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_imageuser_default(ImageUser *iuser)
void BKE_image_partial_update_mark_full_update(Image *image)
Mark the whole image to be updated.
DerivedMesh * CDDM_from_mesh(Mesh *mesh)
void DM_set_only_copy(DerivedMesh *dm, const CustomData_MeshMasks *mask)
bool BKE_modifier_is_enabled(const Scene *scene, ModifierData *md, int required_mode)
MultiresModifierData * get_multires_modifier(Scene *scene, Object *ob, bool use_first)
Definition multires.cc:296
void multires_flush_sculpt_updates(Object *object)
Definition multires.cc:388
DerivedMesh * multires_make_derived_from_derived(DerivedMesh *dm, MultiresModifierData *mmd, Scene *scene, Object *ob, MultiresFlags flags)
Definition multires.cc:1193
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
int BKE_scene_num_threads(const Scene *scene)
Definition scene.cc:2922
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
#define ELEM(...)
@ ID_TAG_DOIT
Definition DNA_ID.h:944
@ CD_PROP_FLOAT2
@ eModifierMode_Realtime
Object is a sort of wrapper for general info.
@ OB_MESH
@ R_IMF_PLANES_RGBA
@ R_BAKE_LORES_MESH
@ R_BAKE_USERSCALE
@ R_BAKE_CLEAR
@ R_BAKE_MULTIRES
@ R_BAKE_EXTEND
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
bool ED_operator_object_active(bContext *C)
bool ED_object_get_active_image(Object *ob, int mat_nr, Image **r_ima, ImageUser **r_iuser, const bNode **r_node, const bNodeTree **r_ntree)
void IMB_rectfill(ImBuf *drect, const float col[4])
Definition rectop.cc:979
Read Guarded memory(de)allocation.
#define RE_BAKE_AO
#define RE_BAKE_NORMALS
#define RE_BAKE_DISPLACEMENT
#define C
Definition RandGen.cpp:29
@ WM_JOB_TYPE_OBJECT_BAKE_TEXTURE
Definition WM_api.hh:1734
@ WM_JOB_EXCL_RENDER
Definition WM_api.hh:1715
@ WM_JOB_PROGRESS
Definition WM_api.hh:1716
@ WM_JOB_PRIORITY
Definition WM_api.hh:1710
#define ND_RENDER_RESULT
Definition WM_types.hh:443
#define NC_SCENE
Definition WM_types.hh:375
#define NC_IMAGE
Definition WM_types.hh:381
BMesh const char void * data
constexpr bool is_empty() const
Definition BLI_span.hh:260
GAttributeReader lookup(const StringRef attribute_id) const
const ccl_global KernelWorkTile * tile
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static ulong * next
#define G(x, y, z)
void RE_multires_bake_images(MultiresBakeRender *bkr)
static wmOperatorStatus objects_bake_render_invoke(bContext *C, wmOperator *op, const wmEvent *)
static void clear_images_poly(Image **ob_image_array, int ob_image_array_len, ClearFlag flag)
static DerivedMesh * multiresbake_create_loresdm(Scene *scene, Object *ob, int *lvl)
static wmOperatorStatus multiresbake_image_exec(bContext *C, wmOperator *op)
static DerivedMesh * multiresbake_create_hiresdm(Scene *scene, Object *ob, int *lvl)
static wmOperatorStatus multiresbake_image_exec_locked(bContext *C, wmOperator *op)
void OBJECT_OT_bake_image(wmOperatorType *ot)
static void multiresbake_startjob(void *bkv, wmJobWorkerStatus *worker_status)
static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
static bool is_multires_bake(Scene *scene)
static void multiresbake_freejob(void *bkv)
static Image * bake_object_image_get(Object *ob, int mat_nr)
static void clear_single_image(Image *image, ClearFlag flag)
static wmOperatorStatus bake_image_exec(bContext *C, wmOperator *op)
static bool multiresbake_check(bContext *C, wmOperator *op)
static wmOperatorStatus objects_bake_render_modal(bContext *C, wmOperator *, const wmEvent *event)
static Image ** bake_object_image_get_array(Object *ob)
void(* release)(DerivedMesh *dm)
int tag
Definition DNA_ID.h:424
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
unsigned char planes
ListBase tiles
void * first
struct ModifierData * next
DerivedMesh * hires_dm
DerivedMesh * lores_dm
struct MultiresBakeRender::@366053046362253255265244177064206014103203005154 ob_image
short bake_margin_type
float bake_user_scale
struct RenderData r
struct blender::ed::object::MultiresBakerJobData::@253045306372077121211153245217170302031260347042 ob_image
wmEventType type
Definition WM_types.hh:754
struct ReportList * reports
i
Definition text_draw.cc:230
void WM_cursor_wait(bool val)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ EVT_ESCKEY
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
Definition wm_jobs.cc:353
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
Definition wm_jobs.cc:456
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
Definition wm_jobs.cc:190
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
Definition wm_jobs.cc:365
bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type)
Definition wm_jobs.cc:224
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))
Definition wm_jobs.cc:337
uint8_t flag
Definition wm_window.cc:139