Blender V4.3
python.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include <Python.h>
6
7#include "blender/CCL_api.h"
8
9#include "blender/device.h"
10#include "blender/session.h"
11#include "blender/sync.h"
12#include "blender/util.h"
13
14#include "session/denoising.h"
15#include "session/merge.h"
16
17#include "util/debug.h"
18#include "util/foreach.h"
19#include "util/guiding.h"
20#include "util/log.h"
21#include "util/md5.h"
23#include "util/path.h"
24#include "util/string.h"
25#include "util/task.h"
26#include "util/tbb.h"
27#include "util/types.h"
28
29#include "GPU_state.hh"
30
31#ifdef WITH_OSL
32# include "scene/osl.h"
33
34# include <OSL/oslconfig.h>
35# include <OSL/oslquery.h>
36#endif
37
38#ifdef WITH_METAL
39# include "device/metal/device.h"
40#endif
41
43
44namespace {
45
46/* Flag describing whether debug flags were synchronized from scene. */
47bool debug_flags_set = false;
48
49void *pylong_as_voidptr_typesafe(PyObject *object)
50{
51 if (object == Py_None) {
52 return NULL;
53 }
54 return PyLong_AsVoidPtr(object);
55}
56
57PyObject *pyunicode_from_string(const char *str)
58{
59 /* Ignore errors if device API returns invalid UTF-8 strings. */
60 return PyUnicode_DecodeUTF8(str, strlen(str), "ignore");
61}
62
63/* Synchronize debug flags from a given Blender scene.
64 * Return truth when device list needs invalidation.
65 */
66static void debug_flags_sync_from_scene(BL::Scene b_scene)
67{
68 DebugFlagsRef flags = DebugFlags();
69 PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
70 /* Synchronize CPU flags. */
71 flags.cpu.avx2 = get_boolean(cscene, "debug_use_cpu_avx2");
72 flags.cpu.sse42 = get_boolean(cscene, "debug_use_cpu_sse42");
73 flags.cpu.bvh_layout = (BVHLayout)get_enum(cscene, "debug_bvh_layout");
74 /* Synchronize CUDA flags. */
75 flags.cuda.adaptive_compile = get_boolean(cscene, "debug_use_cuda_adaptive_compile");
76 /* Synchronize OptiX flags. */
77 flags.optix.use_debug = get_boolean(cscene, "debug_use_optix_debug");
78}
79
80/* Reset debug flags to default values.
81 * Return truth when device list needs invalidation.
82 */
83static void debug_flags_reset()
84{
85 DebugFlagsRef flags = DebugFlags();
86 flags.reset();
87}
88
89} /* namespace */
90
91void python_thread_state_save(void **python_thread_state)
92{
93 *python_thread_state = (void *)PyEval_SaveThread();
94}
95
96void python_thread_state_restore(void **python_thread_state)
97{
98 PyEval_RestoreThread((PyThreadState *)*python_thread_state);
99 *python_thread_state = NULL;
100}
101
102static const char *PyC_UnicodeAsBytes(PyObject *py_str, PyObject **coerce)
103{
104 const char *result = PyUnicode_AsUTF8(py_str);
105 if (result) {
106 /* 99% of the time this is enough but we better support non unicode
107 * chars since blender doesn't limit this.
108 */
109 return result;
110 }
111 else {
112 PyErr_Clear();
113 if (PyBytes_Check(py_str)) {
114 return PyBytes_AS_STRING(py_str);
115 }
116 else if ((*coerce = PyUnicode_EncodeFSDefault(py_str))) {
117 return PyBytes_AS_STRING(*coerce);
118 }
119 else {
120 /* Clear the error, so Cycles can be at least used without
121 * GPU and OSL support,
122 */
123 PyErr_Clear();
124 return "";
125 }
126 }
127}
128
129static PyObject *init_func(PyObject * /*self*/, PyObject *args)
130{
131 PyObject *path, *user_path;
132 int headless;
133
134 if (!PyArg_ParseTuple(args, "OOi", &path, &user_path, &headless)) {
135 return nullptr;
136 }
137
138 PyObject *path_coerce = nullptr, *user_path_coerce = nullptr;
139 path_init(PyC_UnicodeAsBytes(path, &path_coerce),
140 PyC_UnicodeAsBytes(user_path, &user_path_coerce));
141 Py_XDECREF(path_coerce);
142 Py_XDECREF(user_path_coerce);
143
144 BlenderSession::headless = headless;
145
146 Py_RETURN_NONE;
147}
148
149static PyObject *exit_func(PyObject * /*self*/, PyObject * /*args*/)
150{
151#ifdef WITH_METAL
153#endif
154
158 Py_RETURN_NONE;
159}
160
161static PyObject *create_func(PyObject * /*self*/, PyObject *args)
162{
163 PyObject *pyengine, *pypreferences, *pydata, *pyscreen, *pyregion, *pyv3d, *pyrv3d;
164 int preview_osl;
165
166 if (!PyArg_ParseTuple(args,
167 "OOOOOOOi",
168 &pyengine,
169 &pypreferences,
170 &pydata,
171 &pyscreen,
172 &pyregion,
173 &pyv3d,
174 &pyrv3d,
175 &preview_osl))
176 {
177 return NULL;
178 }
179
180 /* RNA */
181 ID *bScreen = (ID *)PyLong_AsVoidPtr(pyscreen);
182
183 PointerRNA engineptr = RNA_pointer_create(
184 NULL, &RNA_RenderEngine, (void *)PyLong_AsVoidPtr(pyengine));
185 BL::RenderEngine engine(engineptr);
186
187 PointerRNA preferencesptr = RNA_pointer_create(
188 NULL, &RNA_Preferences, (void *)PyLong_AsVoidPtr(pypreferences));
189 BL::Preferences preferences(preferencesptr);
190
191 PointerRNA dataptr = RNA_main_pointer_create((Main *)PyLong_AsVoidPtr(pydata));
192 BL::BlendData data(dataptr);
193
194 PointerRNA regionptr = RNA_pointer_create(
195 bScreen, &RNA_Region, pylong_as_voidptr_typesafe(pyregion));
196 BL::Region region(regionptr);
197
199 bScreen, &RNA_SpaceView3D, pylong_as_voidptr_typesafe(pyv3d));
200 BL::SpaceView3D v3d(v3dptr);
201
203 bScreen, &RNA_RegionView3D, pylong_as_voidptr_typesafe(pyrv3d));
204 BL::RegionView3D rv3d(rv3dptr);
205
206 /* create session */
207 BlenderSession *session;
208
209 if (rv3d) {
210 /* interactive viewport session */
211 int width = region.width();
212 int height = region.height();
213
214 session = new BlenderSession(engine, preferences, data, v3d, rv3d, width, height);
215 }
216 else {
217 /* offline session or preview render */
218 session = new BlenderSession(engine, preferences, data, preview_osl);
219 }
220
221 return PyLong_FromVoidPtr(session);
222}
223
224static PyObject *free_func(PyObject * /*self*/, PyObject *value)
225{
226 delete (BlenderSession *)PyLong_AsVoidPtr(value);
227
228 Py_RETURN_NONE;
229}
230
231static PyObject *render_func(PyObject * /*self*/, PyObject *args)
232{
233 PyObject *pysession, *pydepsgraph;
234
235 if (!PyArg_ParseTuple(args, "OO", &pysession, &pydepsgraph)) {
236 return NULL;
237 }
238
239 BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
240
241 PointerRNA depsgraphptr = RNA_pointer_create(
242 NULL, &RNA_Depsgraph, (ID *)PyLong_AsVoidPtr(pydepsgraph));
243 BL::Depsgraph b_depsgraph(depsgraphptr);
244
245 /* Allow Blender to execute other Python scripts. */
247
248 session->render(b_depsgraph);
249
251
252 Py_RETURN_NONE;
253}
254
255static PyObject *render_frame_finish_func(PyObject * /*self*/, PyObject *args)
256{
257 PyObject *pysession;
258
259 if (!PyArg_ParseTuple(args, "O", &pysession)) {
260 return nullptr;
261 }
262
263 BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
264
265 /* Allow Blender to execute other Python scripts. */
267
268 session->render_frame_finish();
269
271
272 Py_RETURN_NONE;
273}
274
275static PyObject *draw_func(PyObject * /*self*/, PyObject *args)
276{
277 PyObject *py_session, *py_graph, *py_screen, *py_space_image;
278
279 if (!PyArg_ParseTuple(args, "OOOO", &py_session, &py_graph, &py_screen, &py_space_image)) {
280 return nullptr;
281 }
282
283 BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(py_session);
284
285 ID *b_screen = (ID *)PyLong_AsVoidPtr(py_screen);
286
287 PointerRNA b_space_image_ptr = RNA_pointer_create(
288 b_screen, &RNA_SpaceImageEditor, pylong_as_voidptr_typesafe(py_space_image));
289 BL::SpaceImageEditor b_space_image(b_space_image_ptr);
290
291 session->draw(b_space_image);
292
293 Py_RETURN_NONE;
294}
295
296/* pixel_array and result passed as pointers */
297static PyObject *bake_func(PyObject * /*self*/, PyObject *args)
298{
299 PyObject *pysession, *pydepsgraph, *pyobject;
300 const char *pass_type;
301 int pass_filter, width, height;
302
303 if (!PyArg_ParseTuple(args,
304 "OOOsiii",
305 &pysession,
306 &pydepsgraph,
307 &pyobject,
308 &pass_type,
309 &pass_filter,
310 &width,
311 &height))
312 {
313 return NULL;
314 }
315
316 BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
317
318 PointerRNA depsgraphptr = RNA_pointer_create(
319 NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph));
320 BL::Depsgraph b_depsgraph(depsgraphptr);
321
322 PointerRNA objectptr = RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyobject));
323 BL::Object b_object(objectptr);
324
326
327 session->bake(b_depsgraph, b_object, pass_type, pass_filter, width, height);
328
330
331 Py_RETURN_NONE;
332}
333
334static PyObject *view_draw_func(PyObject * /*self*/, PyObject *args)
335{
336 PyObject *pysession, *pygraph, *pyv3d, *pyrv3d;
337
338 if (!PyArg_ParseTuple(args, "OOOO", &pysession, &pygraph, &pyv3d, &pyrv3d)) {
339 return NULL;
340 }
341
342 BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
343
344 if (PyLong_AsVoidPtr(pyrv3d)) {
345 /* 3d view drawing */
346 int viewport[4];
347 GPU_viewport_size_get_i(viewport);
348
349 session->view_draw(viewport[2], viewport[3]);
350 }
351
352 Py_RETURN_NONE;
353}
354
355static PyObject *reset_func(PyObject * /*self*/, PyObject *args)
356{
357 PyObject *pysession, *pydata, *pydepsgraph;
358
359 if (!PyArg_ParseTuple(args, "OOO", &pysession, &pydata, &pydepsgraph)) {
360 return NULL;
361 }
362
363 BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
364
365 PointerRNA dataptr = RNA_main_pointer_create((Main *)PyLong_AsVoidPtr(pydata));
366 BL::BlendData b_data(dataptr);
367
368 PointerRNA depsgraphptr = RNA_pointer_create(
369 NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph));
370 BL::Depsgraph b_depsgraph(depsgraphptr);
371
373
374 session->reset_session(b_data, b_depsgraph);
375
377
378 Py_RETURN_NONE;
379}
380
381static PyObject *sync_func(PyObject * /*self*/, PyObject *args)
382{
383 PyObject *pysession, *pydepsgraph;
384
385 if (!PyArg_ParseTuple(args, "OO", &pysession, &pydepsgraph)) {
386 return NULL;
387 }
388
389 BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession);
390
391 PointerRNA depsgraphptr = RNA_pointer_create(
392 NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph));
393 BL::Depsgraph b_depsgraph(depsgraphptr);
394
396
397 session->synchronize(b_depsgraph);
398
400
401 Py_RETURN_NONE;
402}
403
404static PyObject *available_devices_func(PyObject * /*self*/, PyObject *args)
405{
406 const char *type_name;
407 if (!PyArg_ParseTuple(args, "s", &type_name)) {
408 return NULL;
409 }
410
411 DeviceType type = Device::type_from_string(type_name);
412 /* "NONE" is defined by the add-on, see: `CyclesPreferences.get_device_types`. */
413 if ((type == DEVICE_NONE) && (strcmp(type_name, "NONE") != 0)) {
414 PyErr_Format(PyExc_ValueError, "Device \"%s\" not known.", type_name);
415 return NULL;
416 }
417
418 uint mask = (type == DEVICE_NONE) ? DEVICE_MASK_ALL : DEVICE_MASK(type);
419 mask |= DEVICE_MASK_CPU;
420
422 PyObject *ret = PyTuple_New(devices.size());
423
424 for (size_t i = 0; i < devices.size(); i++) {
425 DeviceInfo &device = devices[i];
426 string type_name = Device::string_from_type(device.type);
427 PyObject *device_tuple = PyTuple_New(7);
428 PyTuple_SET_ITEM(device_tuple, 0, pyunicode_from_string(device.description.c_str()));
429 PyTuple_SET_ITEM(device_tuple, 1, pyunicode_from_string(type_name.c_str()));
430 PyTuple_SET_ITEM(device_tuple, 2, pyunicode_from_string(device.id.c_str()));
431 PyTuple_SET_ITEM(device_tuple, 3, PyBool_FromLong(device.has_peer_memory));
432 PyTuple_SET_ITEM(device_tuple, 4, PyBool_FromLong(device.use_hardware_raytracing));
433 PyTuple_SET_ITEM(
434 device_tuple, 5, PyBool_FromLong(device.denoisers & DENOISER_OPENIMAGEDENOISE));
435 PyTuple_SET_ITEM(device_tuple, 6, PyBool_FromLong(device.denoisers & DENOISER_OPTIX));
436 PyTuple_SET_ITEM(ret, i, device_tuple);
437 }
438
439 return ret;
440}
441
442#ifdef WITH_OSL
443
444static PyObject *osl_update_node_func(PyObject * /*self*/, PyObject *args)
445{
446 PyObject *pydata, *pynodegroup, *pynode;
447 const char *filepath = NULL;
448
449 if (!PyArg_ParseTuple(args, "OOOs", &pydata, &pynodegroup, &pynode, &filepath))
450 return NULL;
451
452 /* RNA */
453 PointerRNA dataptr = RNA_main_pointer_create((Main *)PyLong_AsVoidPtr(pydata));
454 BL::BlendData b_data(dataptr);
455
456 PointerRNA nodeptr = RNA_pointer_create((ID *)PyLong_AsVoidPtr(pynodegroup),
457 &RNA_ShaderNodeScript,
458 (void *)PyLong_AsVoidPtr(pynode));
459 BL::ShaderNodeScript b_node(nodeptr);
460
461 /* update bytecode hash */
462 string bytecode = b_node.bytecode();
463
464 if (!bytecode.empty()) {
465 MD5Hash md5;
466 md5.append((const uint8_t *)bytecode.c_str(), bytecode.size());
467 b_node.bytecode_hash(md5.get_hex().c_str());
468 }
469 else
470 b_node.bytecode_hash("");
471
472 /* query from file path */
473 OSL::OSLQuery query;
474
475 if (!OSLShaderManager::osl_query(query, filepath))
476 Py_RETURN_FALSE;
477
478 /* add new sockets from parameters */
479 set<void *> used_sockets;
480
481 for (int i = 0; i < query.nparams(); i++) {
482 const OSL::OSLQuery::Parameter *param = query.getparam(i);
483
484 /* skip unsupported types */
485 if (param->varlenarray || param->isstruct || param->type.arraylen > 1)
486 continue;
487
488 /* Read metadata. */
489 bool is_bool_param = false;
490 bool hide_value = !param->validdefault;
491 ustring param_label = param->name;
492
493 for (const OSL::OSLQuery::Parameter &metadata : param->metadata) {
494 if (metadata.type == TypeDesc::STRING) {
495 if (metadata.name == "widget") {
496 /* Boolean socket. */
497 if (metadata.sdefault[0] == "boolean" || metadata.sdefault[0] == "checkBox") {
498 is_bool_param = true;
499 }
500 else if (metadata.sdefault[0] == "null") {
501 hide_value = true;
502 }
503 }
504 else if (metadata.name == "label") {
505 /* Socket label. */
506 param_label = metadata.sdefault[0];
507 }
508 }
509 }
510 /* determine socket type */
511 string socket_type;
512 BL::NodeSocket::type_enum data_type = BL::NodeSocket::type_VALUE;
513 float4 default_float4 = make_float4(0.0f, 0.0f, 0.0f, 1.0f);
514 float default_float = 0.0f;
515 int default_int = 0;
516 string default_string = "";
517 bool default_boolean = false;
518
519 if (param->isclosure) {
520 socket_type = "NodeSocketShader";
521 data_type = BL::NodeSocket::type_SHADER;
522 }
523 else if (param->type.vecsemantics == TypeDesc::COLOR) {
524 socket_type = "NodeSocketColor";
525 data_type = BL::NodeSocket::type_RGBA;
526
527 if (param->validdefault) {
528 default_float4[0] = param->fdefault[0];
529 default_float4[1] = param->fdefault[1];
530 default_float4[2] = param->fdefault[2];
531 }
532 }
533 else if (param->type.vecsemantics == TypeDesc::POINT ||
534 param->type.vecsemantics == TypeDesc::VECTOR ||
535 param->type.vecsemantics == TypeDesc::NORMAL)
536 {
537 socket_type = "NodeSocketVector";
538 data_type = BL::NodeSocket::type_VECTOR;
539
540 if (param->validdefault) {
541 default_float4[0] = param->fdefault[0];
542 default_float4[1] = param->fdefault[1];
543 default_float4[2] = param->fdefault[2];
544 }
545 }
546 else if (param->type.aggregate == TypeDesc::SCALAR) {
547 if (param->type.basetype == TypeDesc::INT) {
548 if (is_bool_param) {
549 socket_type = "NodeSocketBool";
550 data_type = BL::NodeSocket::type_BOOLEAN;
551 if (param->validdefault) {
552 default_boolean = bool(param->idefault[0]);
553 }
554 }
555 else {
556 socket_type = "NodeSocketInt";
557 data_type = BL::NodeSocket::type_INT;
558 if (param->validdefault)
559 default_int = param->idefault[0];
560 }
561 }
562 else if (param->type.basetype == TypeDesc::FLOAT) {
563 socket_type = "NodeSocketFloat";
564 data_type = BL::NodeSocket::type_VALUE;
565 if (param->validdefault)
566 default_float = param->fdefault[0];
567 }
568 else if (param->type.basetype == TypeDesc::STRING) {
569 socket_type = "NodeSocketString";
570 data_type = BL::NodeSocket::type_STRING;
571 if (param->validdefault)
572 default_string = param->sdefault[0].string();
573 }
574 else
575 continue;
576 }
577 else
578 continue;
579
580 /* Update existing socket. */
581 bool found_existing = false;
582 if (param->isoutput) {
583 for (BL::NodeSocket &b_sock : b_node.outputs) {
584 if (b_sock.identifier() == param->name) {
585 if (b_sock.bl_idname() != socket_type) {
586 /* Remove if type no longer matches. */
587 b_node.outputs.remove(b_data, b_sock);
588 }
589 else {
590 /* Reuse and update label. */
591 if (b_sock.name() != param_label) {
592 b_sock.name(param_label.string());
593 }
594 used_sockets.insert(b_sock.ptr.data);
595 found_existing = true;
596 }
597 break;
598 }
599 }
600 }
601 else {
602 for (BL::NodeSocket &b_sock : b_node.inputs) {
603 if (b_sock.identifier() == param->name) {
604 if (b_sock.bl_idname() != socket_type) {
605 /* Remove if type no longer matches. */
606 b_node.inputs.remove(b_data, b_sock);
607 }
608 else {
609 /* Reuse and update label. */
610 if (b_sock.name() != param_label) {
611 b_sock.name(param_label.string());
612 }
613 if (b_sock.hide_value() != hide_value) {
614 b_sock.hide_value(hide_value);
615 }
616 used_sockets.insert(b_sock.ptr.data);
617 found_existing = true;
618 }
619 break;
620 }
621 }
622 }
623
624 if (!found_existing) {
625 /* Create new socket. */
626 BL::NodeSocket b_sock =
627 (param->isoutput) ?
628 b_node.outputs.create(
629 b_data, socket_type.c_str(), param_label.c_str(), param->name.c_str(), false) :
630 b_node.inputs.create(
631 b_data, socket_type.c_str(), param_label.c_str(), param->name.c_str(), false);
632
633 /* set default value */
634 if (data_type == BL::NodeSocket::type_VALUE) {
635 set_float(b_sock.ptr, "default_value", default_float);
636 }
637 else if (data_type == BL::NodeSocket::type_INT) {
638 set_int(b_sock.ptr, "default_value", default_int);
639 }
640 else if (data_type == BL::NodeSocket::type_RGBA) {
641 set_float4(b_sock.ptr, "default_value", default_float4);
642 }
643 else if (data_type == BL::NodeSocket::type_VECTOR) {
644 set_float3(b_sock.ptr, "default_value", float4_to_float3(default_float4));
645 }
646 else if (data_type == BL::NodeSocket::type_STRING) {
647 set_string(b_sock.ptr, "default_value", default_string);
648 }
649 else if (data_type == BL::NodeSocket::type_BOOLEAN) {
650 set_boolean(b_sock.ptr, "default_value", default_boolean);
651 }
652
653 b_sock.hide_value(hide_value);
654
655 used_sockets.insert(b_sock.ptr.data);
656 }
657 }
658
659 /* remove unused parameters */
660 bool removed;
661
662 do {
663 removed = false;
664
665 for (BL::NodeSocket &b_input : b_node.inputs) {
666 if (used_sockets.find(b_input.ptr.data) == used_sockets.end()) {
667 b_node.inputs.remove(b_data, b_input);
668 removed = true;
669 break;
670 }
671 }
672
673 for (BL::NodeSocket &b_output : b_node.outputs) {
674 if (used_sockets.find(b_output.ptr.data) == used_sockets.end()) {
675 b_node.outputs.remove(b_data, b_output);
676 removed = true;
677 break;
678 }
679 }
680 } while (removed);
681
682 Py_RETURN_TRUE;
683}
684
685static PyObject *osl_compile_func(PyObject * /*self*/, PyObject *args)
686{
687 const char *inputfile = NULL, *outputfile = NULL;
688
689 if (!PyArg_ParseTuple(args, "ss", &inputfile, &outputfile))
690 return NULL;
691
692 /* return */
693 if (!OSLShaderManager::osl_compile(inputfile, outputfile))
694 Py_RETURN_FALSE;
695
696 Py_RETURN_TRUE;
697}
698#endif
699
700static PyObject *system_info_func(PyObject * /*self*/, PyObject * /*value*/)
701{
702 string system_info = Device::device_capabilities();
703 return pyunicode_from_string(system_info.c_str());
704}
705
706static bool image_parse_filepaths(PyObject *pyfilepaths, vector<string> &filepaths)
707{
708 if (PyUnicode_Check(pyfilepaths)) {
709 const char *filepath = PyUnicode_AsUTF8(pyfilepaths);
710 filepaths.push_back(filepath);
711 return true;
712 }
713
714 PyObject *sequence = PySequence_Fast(pyfilepaths,
715 "File paths must be a string or sequence of strings");
716 if (sequence == NULL) {
717 return false;
718 }
719
720 for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(sequence); i++) {
721 PyObject *item = PySequence_Fast_GET_ITEM(sequence, i);
722 const char *filepath = PyUnicode_AsUTF8(item);
723 if (filepath == NULL) {
724 PyErr_SetString(PyExc_ValueError, "File paths must be a string or sequence of strings.");
725 Py_DECREF(sequence);
726 return false;
727 }
728 filepaths.push_back(filepath);
729 }
730 Py_DECREF(sequence);
731
732 return true;
733}
734
735static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *keywords)
736{
737 static const char *keyword_list[] = {
738 "preferences", "scene", "view_layer", "input", "output", NULL};
739 PyObject *pypreferences, *pyscene, *pyviewlayer;
740 PyObject *pyinput, *pyoutput = NULL;
741
742 if (!PyArg_ParseTupleAndKeywords(args,
743 keywords,
744 "OOOO|O",
745 (char **)keyword_list,
746 &pypreferences,
747 &pyscene,
748 &pyviewlayer,
749 &pyinput,
750 &pyoutput))
751 {
752 return NULL;
753 }
754
755 /* Get device specification from preferences and scene. */
756 PointerRNA preferencesptr = RNA_pointer_create(
757 NULL, &RNA_Preferences, (void *)PyLong_AsVoidPtr(pypreferences));
758 BL::Preferences b_preferences(preferencesptr);
759
760 PointerRNA sceneptr = RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyscene));
761 BL::Scene b_scene(sceneptr);
762
763 DeviceInfo preferences_device;
764 DeviceInfo pathtrace_device = blender_device_info(
765 b_preferences, b_scene, true, true, preferences_device);
766
767 /* Get denoising parameters from view layer. */
768 PointerRNA viewlayerptr = RNA_pointer_create(
769 (ID *)PyLong_AsVoidPtr(pyscene), &RNA_ViewLayer, PyLong_AsVoidPtr(pyviewlayer));
770 BL::ViewLayer b_view_layer(viewlayerptr);
771
773 b_scene, b_view_layer, true, preferences_device);
774 params.use = true;
775
776 /* Parse file paths list. */
777 vector<string> input, output;
778
779 if (!image_parse_filepaths(pyinput, input)) {
780 return NULL;
781 }
782
783 if (pyoutput) {
784 if (!image_parse_filepaths(pyoutput, output)) {
785 return NULL;
786 }
787 }
788 else {
789 output = input;
790 }
791
792 if (input.empty()) {
793 PyErr_SetString(PyExc_ValueError, "No input file paths specified.");
794 return NULL;
795 }
796 if (input.size() != output.size()) {
797 PyErr_SetString(PyExc_ValueError, "Number of input and output file paths does not match.");
798 return NULL;
799 }
800
801 /* Create denoiser. */
802 /* We are using preference device here, because path trace device will be identical to it unless
803 * scene is setting CPU render or command line override render device. But both of this options
804 * are for render, not for denoising. */
805 DenoiserPipeline denoiser(preferences_device, params);
806 denoiser.input = input;
807 denoiser.output = output;
808
809 /* Run denoiser. */
810 if (!denoiser.run()) {
811 PyErr_SetString(PyExc_ValueError, denoiser.error.c_str());
812 return NULL;
813 }
814
815 Py_RETURN_NONE;
816}
817
818static PyObject *merge_func(PyObject * /*self*/, PyObject *args, PyObject *keywords)
819{
820 static const char *keyword_list[] = {"input", "output", NULL};
821 PyObject *pyinput, *pyoutput = NULL;
822
823 if (!PyArg_ParseTupleAndKeywords(
824 args, keywords, "OO", (char **)keyword_list, &pyinput, &pyoutput))
825 {
826 return NULL;
827 }
828
829 /* Parse input list. */
830 vector<string> input;
831 if (!image_parse_filepaths(pyinput, input)) {
832 return NULL;
833 }
834
835 /* Parse output string. */
836 if (!PyUnicode_Check(pyoutput)) {
837 PyErr_SetString(PyExc_ValueError, "Output must be a string.");
838 return NULL;
839 }
840 string output = PyUnicode_AsUTF8(pyoutput);
841
842 /* Merge. */
843 ImageMerger merger;
844 merger.input = input;
845 merger.output = output;
846
847 if (!merger.run()) {
848 PyErr_SetString(PyExc_ValueError, merger.error.c_str());
849 return NULL;
850 }
851
852 Py_RETURN_NONE;
853}
854
855static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args)
856{
857 PyObject *pyscene;
858 if (!PyArg_ParseTuple(args, "O", &pyscene)) {
859 return NULL;
860 }
861
862 PointerRNA sceneptr = RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyscene));
863 BL::Scene b_scene(sceneptr);
864
865 debug_flags_sync_from_scene(b_scene);
866
867 debug_flags_set = true;
868
869 Py_RETURN_NONE;
870}
871
872static PyObject *debug_flags_reset_func(PyObject * /*self*/, PyObject * /*args*/)
873{
874 debug_flags_reset();
875 if (debug_flags_set) {
876 debug_flags_set = false;
877 }
878 Py_RETURN_NONE;
879}
880
881static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*/)
882{
884 Py_RETURN_NONE;
885}
886
887static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/)
888{
890 bool has_cuda = false, has_optix = false, has_hip = false, has_metal = false, has_oneapi = false,
891 has_hiprt = false;
892 foreach (DeviceType device_type, device_types) {
893 has_cuda |= (device_type == DEVICE_CUDA);
894 has_optix |= (device_type == DEVICE_OPTIX);
895 has_hip |= (device_type == DEVICE_HIP);
896 has_metal |= (device_type == DEVICE_METAL);
897 has_oneapi |= (device_type == DEVICE_ONEAPI);
898 has_hiprt |= (device_type == DEVICE_HIPRT);
899 }
900 PyObject *list = PyTuple_New(6);
901 PyTuple_SET_ITEM(list, 0, PyBool_FromLong(has_cuda));
902 PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_optix));
903 PyTuple_SET_ITEM(list, 2, PyBool_FromLong(has_hip));
904 PyTuple_SET_ITEM(list, 3, PyBool_FromLong(has_metal));
905 PyTuple_SET_ITEM(list, 4, PyBool_FromLong(has_oneapi));
906 PyTuple_SET_ITEM(list, 5, PyBool_FromLong(has_hiprt));
907 return list;
908}
909
910static PyObject *set_device_override_func(PyObject * /*self*/, PyObject *arg)
911{
912 PyObject *override_string = PyObject_Str(arg);
913 string override = PyUnicode_AsUTF8(override_string);
914 Py_DECREF(override_string);
915
916 bool include_cpu = false;
917 const string cpu_suffix = "+CPU";
918 if (string_endswith(override, cpu_suffix)) {
919 include_cpu = true;
920 override = override.substr(0, override.length() - cpu_suffix.length());
921 }
922
923 if (override == "CPU") {
925 }
926 else if (override == "CUDA") {
928 }
929 else if (override == "OPTIX") {
931 }
932 else if (override == "HIP") {
934 }
935 else if (override == "METAL") {
937 }
938 else if (override == "ONEAPI") {
940 }
941 else {
942 printf("\nError: %s is not a valid Cycles device.\n", override.c_str());
943 Py_RETURN_FALSE;
944 }
945
946 if (include_cpu) {
949 }
950
951 Py_RETURN_TRUE;
952}
953
954static PyMethodDef methods[] = {
955 {"init", init_func, METH_VARARGS, ""},
956 {"exit", exit_func, METH_VARARGS, ""},
957 {"create", create_func, METH_VARARGS, ""},
958 {"free", free_func, METH_O, ""},
959 {"render", render_func, METH_VARARGS, ""},
960 {"render_frame_finish", render_frame_finish_func, METH_VARARGS, ""},
961 {"draw", draw_func, METH_VARARGS, ""},
962 {"bake", bake_func, METH_VARARGS, ""},
963 {"view_draw", view_draw_func, METH_VARARGS, ""},
964 {"sync", sync_func, METH_VARARGS, ""},
965 {"reset", reset_func, METH_VARARGS, ""},
966#ifdef WITH_OSL
967 {"osl_update_node", osl_update_node_func, METH_VARARGS, ""},
968 {"osl_compile", osl_compile_func, METH_VARARGS, ""},
969#endif
970 {"available_devices", available_devices_func, METH_VARARGS, ""},
971 {"system_info", system_info_func, METH_NOARGS, ""},
972
973 /* Standalone denoising */
974 {"denoise", (PyCFunction)denoise_func, METH_VARARGS | METH_KEYWORDS, ""},
975 {"merge", (PyCFunction)merge_func, METH_VARARGS | METH_KEYWORDS, ""},
976
977 /* Debugging routines */
978 {"debug_flags_update", debug_flags_update_func, METH_VARARGS, ""},
979 {"debug_flags_reset", debug_flags_reset_func, METH_NOARGS, ""},
980
981 /* Statistics. */
982 {"enable_print_stats", enable_print_stats_func, METH_NOARGS, ""},
983
984 /* Compute Device selection */
985 {"get_device_types", get_device_types_func, METH_VARARGS, ""},
986 {"set_device_override", set_device_override_func, METH_O, ""},
987
988 {NULL, NULL, 0, NULL},
989};
990
991static struct PyModuleDef module = {
992 /*m_base*/ PyModuleDef_HEAD_INIT,
993 /*m_name*/ "_cycles",
994 /*m_doc*/ "Blender cycles render integration",
995 /*m_size*/ -1,
996 /*m_methods*/ methods,
997 /*m_slots*/ nullptr,
998 /*m_traverse*/ nullptr,
999 /*m_clear*/ nullptr,
1000 /*m_free*/ nullptr,
1001};
1002
1004
1006{
1007 PyObject *mod = PyModule_Create(&ccl::module);
1008
1009#ifdef WITH_OSL
1010 /* TODO(sergey): This gives us library we've been linking against.
1011 * In theory with dynamic OSL library it might not be
1012 * accurate, but there's nothing in OSL API which we
1013 * might use to get version in runtime.
1014 */
1015 int curversion = OSL_LIBRARY_VERSION_CODE;
1016 PyModule_AddObjectRef(mod, "with_osl", Py_True);
1017 PyModule_AddObject(
1018 mod,
1019 "osl_version",
1020 Py_BuildValue("(iii)", curversion / 10000, (curversion / 100) % 100, curversion % 100));
1021 PyModule_AddObject(
1022 mod,
1023 "osl_version_string",
1024 PyUnicode_FromFormat(
1025 "%2d, %2d, %2d", curversion / 10000, (curversion / 100) % 100, curversion % 100));
1026#else
1027 PyModule_AddObjectRef(mod, "with_osl", Py_False);
1028 PyModule_AddStringConstant(mod, "osl_version", "unknown");
1029 PyModule_AddStringConstant(mod, "osl_version_string", "unknown");
1030#endif
1031
1032 if (ccl::guiding_supported()) {
1033 PyModule_AddObjectRef(mod, "with_path_guiding", Py_True);
1034 }
1035 else {
1036 PyModule_AddObjectRef(mod, "with_path_guiding", Py_False);
1037 }
1038
1039#ifdef WITH_EMBREE
1040 PyModule_AddObjectRef(mod, "with_embree", Py_True);
1041#else /* WITH_EMBREE */
1042 PyModule_AddObjectRef(mod, "with_embree", Py_False);
1043#endif /* WITH_EMBREE */
1044
1045#ifdef WITH_EMBREE_GPU
1046 PyModule_AddObjectRef(mod, "with_embree_gpu", Py_True);
1047#else /* WITH_EMBREE_GPU */
1048 PyModule_AddObjectRef(mod, "with_embree_gpu", Py_False);
1049#endif /* WITH_EMBREE_GPU */
1050
1051 if (ccl::openimagedenoise_supported()) {
1052 PyModule_AddObjectRef(mod, "with_openimagedenoise", Py_True);
1053 }
1054 else {
1055 PyModule_AddObjectRef(mod, "with_openimagedenoise", Py_False);
1056 }
1057
1058#ifdef WITH_CYCLES_DEBUG
1059 PyModule_AddObjectRef(mod, "with_debug", Py_True);
1060#else /* WITH_CYCLES_DEBUG */
1061 PyModule_AddObjectRef(mod, "with_debug", Py_False);
1062#endif /* WITH_CYCLES_DEBUG */
1063
1064 return (void *)mod;
1065}
unsigned int uint
void GPU_viewport_size_get_i(int coords[4])
Definition gpu_state.cc:271
DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scene, bool background, bool preview, DeviceInfo &preferences_device)
#define output
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
Definition btVector3.h:257
static bool headless
static bool print_render_stats
void synchronize(BL::Depsgraph &b_depsgraph)
void view_draw(int w, int h)
void reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsgraph)
void bake(BL::Depsgraph &b_depsgrah, BL::Object &b_object, const string &pass_type, const int custom_flag, const int bake_width, const int bake_height)
void render(BL::Depsgraph &b_depsgraph)
void draw(BL::SpaceImageEditor &space_image)
static DeviceTypeMask device_override
static DenoiseParams get_denoise_params(BL::Scene &b_scene, BL::ViewLayer &b_view_layer, bool background, const DeviceInfo &denoise_device)
Definition sync.cpp:988
CPU cpu
Definition debug.h:117
void reset()
Definition debug.cpp:104
OptiX optix
Definition debug.h:123
CUDA cuda
Definition debug.h:120
vector< string > input
Definition denoising.h:37
vector< string > output
Definition denoising.h:41
DenoiserTypeMask denoisers
bool has_peer_memory
DeviceType type
string description
bool use_hardware_raytracing
static void free_memory()
static vector< DeviceInfo > available_devices(uint device_type_mask=DEVICE_MASK_ALL)
static DeviceType type_from_string(const char *name)
static string device_capabilities(uint device_type_mask=DEVICE_MASK_ALL)
static vector< DeviceType > available_types()
static string string_from_type(DeviceType type)
bool run()
Definition merge.cpp:575
string output
Definition merge.h:26
vector< string > input
Definition merge.h:24
string error
Definition merge.h:21
Definition md5.h:21
string get_hex()
Definition md5.cpp:354
void append(const uint8_t *data, int size)
Definition md5.cpp:255
static void free_memory()
static void free_memory()
Definition task.cpp:93
#define printf
static void set_boolean(PointerRNA &ptr, const char *name, bool value)
static bool get_boolean(PointerRNA &ptr, const char *name)
static void set_float3(PointerRNA &ptr, const char *name, float3 value)
static int get_enum(PointerRNA &ptr, const char *name, int num_values=-1, int default_value=-1)
static void set_float4(PointerRNA &ptr, const char *name, float4 value)
static void set_int(PointerRNA &ptr, const char *name, int value)
static void set_float(PointerRNA &ptr, const char *name, float value)
static void set_string(PointerRNA &ptr, const char *name, const string &value)
DebugFlags & DebugFlags()
Definition debug.h:142
@ DENOISER_OPTIX
Definition denoise.h:14
@ DENOISER_OPENIMAGEDENOISE
Definition denoise.h:15
#define CCL_NAMESPACE_END
#define DEVICE_MASK(type)
DeviceTypeMask
@ DEVICE_MASK_OPTIX
@ DEVICE_MASK_CPU
@ DEVICE_MASK_HIP
@ DEVICE_MASK_ALL
@ DEVICE_MASK_CUDA
@ DEVICE_MASK_METAL
@ DEVICE_MASK_ONEAPI
DeviceType
@ DEVICE_NONE
@ DEVICE_METAL
@ DEVICE_CUDA
@ DEVICE_HIPRT
@ DEVICE_OPTIX
@ DEVICE_HIP
@ DEVICE_ONEAPI
ccl_device_forceinline float4 make_float4(const float x, const float y, const float z, const float w)
#define NULL
void device_metal_exit()
static KeywordTokenDef keyword_list[]
#define str(s)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static void debug_flags_reset()
Definition python.cpp:83
static void debug_flags_sync_from_scene(BL::Scene b_scene)
Definition python.cpp:66
void * pylong_as_voidptr_typesafe(PyObject *object)
Definition python.cpp:49
PyObject * pyunicode_from_string(const char *str)
Definition python.cpp:57
VecBase< float, 4 > float4
CCL_NAMESPACE_BEGIN typedef KernelBVHLayout BVHLayout
Definition params.h:23
void path_init(const string &path, const string &user_path)
Definition path.cpp:326
static PyObject * init_func(PyObject *, PyObject *args)
Definition python.cpp:129
static PyObject * merge_func(PyObject *, PyObject *args, PyObject *keywords)
Definition python.cpp:818
static PyObject * sync_func(PyObject *, PyObject *args)
Definition python.cpp:381
static PyObject * denoise_func(PyObject *, PyObject *args, PyObject *keywords)
Definition python.cpp:735
void python_thread_state_restore(void **python_thread_state)
Definition python.cpp:96
static PyObject * system_info_func(PyObject *, PyObject *)
Definition python.cpp:700
static PyObject * set_device_override_func(PyObject *, PyObject *arg)
Definition python.cpp:910
static PyObject * free_func(PyObject *, PyObject *value)
Definition python.cpp:224
CCL_NAMESPACE_END void * CCL_python_module_init()
Definition python.cpp:1005
static struct PyModuleDef module
Definition python.cpp:991
static bool image_parse_filepaths(PyObject *pyfilepaths, vector< string > &filepaths)
Definition python.cpp:706
static PyMethodDef methods[]
Definition python.cpp:954
static PyObject * available_devices_func(PyObject *, PyObject *args)
Definition python.cpp:404
static PyObject * bake_func(PyObject *, PyObject *args)
Definition python.cpp:297
static PyObject * reset_func(PyObject *, PyObject *args)
Definition python.cpp:355
static PyObject * view_draw_func(PyObject *, PyObject *args)
Definition python.cpp:334
static PyObject * enable_print_stats_func(PyObject *, PyObject *)
Definition python.cpp:881
static PyObject * debug_flags_reset_func(PyObject *, PyObject *)
Definition python.cpp:872
static PyObject * render_func(PyObject *, PyObject *args)
Definition python.cpp:231
static PyObject * debug_flags_update_func(PyObject *, PyObject *args)
Definition python.cpp:855
void python_thread_state_save(void **python_thread_state)
Definition python.cpp:91
static PyObject * draw_func(PyObject *, PyObject *args)
Definition python.cpp:275
static PyObject * get_device_types_func(PyObject *, PyObject *)
Definition python.cpp:887
static const char * PyC_UnicodeAsBytes(PyObject *py_str, PyObject **coerce)
Definition python.cpp:102
static PyObject * render_frame_finish_func(PyObject *, PyObject *args)
Definition python.cpp:255
static PyObject * create_func(PyObject *, PyObject *args)
Definition python.cpp:161
static PyObject * exit_func(PyObject *, PyObject *)
Definition python.cpp:149
return ret
PointerRNA RNA_pointer_get(PointerRNA *ptr, const char *name)
PointerRNA RNA_main_pointer_create(Main *main)
PointerRNA RNA_pointer_create(ID *id, StructRNA *type, void *data)
PointerRNA RNA_id_pointer_create(ID *id)
unsigned char uint8_t
Definition stdint.h:78
bool string_endswith(const string_view s, const string_view end)
Definition string.cpp:114
BVHLayout bvh_layout
Definition debug.h:48
bool adaptive_compile
Definition debug.h:60
Definition DNA_ID.h:413
ccl_device_inline int mod(int x, int m)
Definition util/math.h:520
ccl_device_inline float3 float4_to_float3(const float4 a)
Definition util/math.h:535