34# include <OSL/oslconfig.h>
35# include <OSL/oslquery.h>
51 if (
object == Py_None) {
54 return PyLong_AsVoidPtr(
object);
60 return PyUnicode_DecodeUTF8(
str, strlen(
str),
"ignore");
93 *python_thread_state = (
void *)PyEval_SaveThread();
98 PyEval_RestoreThread((PyThreadState *)*python_thread_state);
99 *python_thread_state =
NULL;
104 const char *result = PyUnicode_AsUTF8(py_str);
113 if (PyBytes_Check(py_str)) {
114 return PyBytes_AS_STRING(py_str);
116 else if ((*coerce = PyUnicode_EncodeFSDefault(py_str))) {
117 return PyBytes_AS_STRING(*coerce);
131 PyObject *path, *user_path;
134 if (!PyArg_ParseTuple(args,
"OOi", &path, &user_path, &headless)) {
138 PyObject *path_coerce =
nullptr, *user_path_coerce =
nullptr;
141 Py_XDECREF(path_coerce);
142 Py_XDECREF(user_path_coerce);
163 PyObject *pyengine, *pypreferences, *pydata, *pyscreen, *pyregion, *pyv3d, *pyrv3d;
166 if (!PyArg_ParseTuple(args,
184 NULL, &RNA_RenderEngine, (
void *)PyLong_AsVoidPtr(pyengine));
185 BL::RenderEngine engine(engineptr);
188 NULL, &RNA_Preferences, (
void *)PyLong_AsVoidPtr(pypreferences));
189 BL::Preferences preferences(preferencesptr);
192 BL::BlendData
data(dataptr);
195 bScreen, &RNA_Region, pylong_as_voidptr_typesafe(pyregion));
196 BL::Region region(regionptr);
199 bScreen, &RNA_SpaceView3D, pylong_as_voidptr_typesafe(pyv3d));
200 BL::SpaceView3D v3d(v3dptr);
203 bScreen, &RNA_RegionView3D, pylong_as_voidptr_typesafe(pyrv3d));
204 BL::RegionView3D rv3d(rv3dptr);
211 int width = region.
width();
212 int height = region.height();
214 session =
new BlenderSession(engine, preferences, data, v3d, rv3d, width, height);
218 session =
new BlenderSession(engine, preferences, data, preview_osl);
221 return PyLong_FromVoidPtr(session);
233 PyObject *pysession, *pydepsgraph;
235 if (!PyArg_ParseTuple(args,
"OO", &pysession, &pydepsgraph)) {
242 NULL, &RNA_Depsgraph, (
ID *)PyLong_AsVoidPtr(pydepsgraph));
243 BL::Depsgraph b_depsgraph(depsgraphptr);
248 session->
render(b_depsgraph);
259 if (!PyArg_ParseTuple(args,
"O", &pysession)) {
277 PyObject *py_session, *py_graph, *py_screen, *py_space_image;
279 if (!PyArg_ParseTuple(args,
"OOOO", &py_session, &py_graph, &py_screen, &py_space_image)) {
285 ID *b_screen = (
ID *)PyLong_AsVoidPtr(py_screen);
288 b_screen, &RNA_SpaceImageEditor, pylong_as_voidptr_typesafe(py_space_image));
289 BL::SpaceImageEditor b_space_image(b_space_image_ptr);
291 session->
draw(b_space_image);
299 PyObject *pysession, *pydepsgraph, *pyobject;
300 const char *pass_type;
301 int pass_filter, width, height;
303 if (!PyArg_ParseTuple(args,
319 NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph));
320 BL::Depsgraph b_depsgraph(depsgraphptr);
323 BL::Object b_object(objectptr);
327 session->
bake(b_depsgraph, b_object, pass_type, pass_filter, width, height);
336 PyObject *pysession, *pygraph, *pyv3d, *pyrv3d;
338 if (!PyArg_ParseTuple(args,
"OOOO", &pysession, &pygraph, &pyv3d, &pyrv3d)) {
344 if (PyLong_AsVoidPtr(pyrv3d)) {
349 session->
view_draw(viewport[2], viewport[3]);
357 PyObject *pysession, *pydata, *pydepsgraph;
359 if (!PyArg_ParseTuple(args,
"OOO", &pysession, &pydata, &pydepsgraph)) {
366 BL::BlendData b_data(dataptr);
369 NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph));
370 BL::Depsgraph b_depsgraph(depsgraphptr);
383 PyObject *pysession, *pydepsgraph;
385 if (!PyArg_ParseTuple(args,
"OO", &pysession, &pydepsgraph)) {
392 NULL, &RNA_Depsgraph, PyLong_AsVoidPtr(pydepsgraph));
393 BL::Depsgraph b_depsgraph(depsgraphptr);
406 const char *type_name;
407 if (!PyArg_ParseTuple(args,
"s", &type_name)) {
413 if ((type ==
DEVICE_NONE) && (strcmp(type_name,
"NONE") != 0)) {
414 PyErr_Format(PyExc_ValueError,
"Device \"%s\" not known.", type_name);
422 PyObject *
ret = PyTuple_New(devices.size());
424 for (
size_t i = 0; i < devices.size(); i++) {
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));
436 PyTuple_SET_ITEM(
ret, i, device_tuple);
444static PyObject *osl_update_node_func(PyObject * , PyObject *args)
446 PyObject *pydata, *pynodegroup, *pynode;
447 const char *filepath =
NULL;
449 if (!PyArg_ParseTuple(args,
"OOOs", &pydata, &pynodegroup, &pynode, &filepath))
454 BL::BlendData b_data(dataptr);
457 &RNA_ShaderNodeScript,
458 (
void *)PyLong_AsVoidPtr(pynode));
459 BL::ShaderNodeScript b_node(nodeptr);
462 string bytecode = b_node.bytecode();
464 if (!bytecode.empty()) {
466 md5.
append((
const uint8_t *)bytecode.c_str(), bytecode.size());
467 b_node.bytecode_hash(md5.
get_hex().c_str());
470 b_node.bytecode_hash(
"");
475 if (!OSLShaderManager::osl_query(query, filepath))
479 set<void *> used_sockets;
481 for (
int i = 0; i < query.nparams(); i++) {
482 const OSL::OSLQuery::Parameter *param = query.getparam(i);
485 if (param->varlenarray || param->isstruct || param->type.arraylen > 1)
489 bool is_bool_param =
false;
490 bool hide_value = !param->validdefault;
491 ustring param_label = param->name;
493 for (
const OSL::OSLQuery::Parameter &metadata : param->metadata) {
494 if (metadata.type == TypeDesc::STRING) {
495 if (metadata.name ==
"widget") {
497 if (metadata.sdefault[0] ==
"boolean" || metadata.sdefault[0] ==
"checkBox") {
498 is_bool_param =
true;
500 else if (metadata.sdefault[0] ==
"null") {
504 else if (metadata.name ==
"label") {
506 param_label = metadata.sdefault[0];
512 BL::NodeSocket::type_enum data_type = BL::NodeSocket::type_VALUE;
514 float default_float = 0.0f;
516 string default_string =
"";
517 bool default_boolean =
false;
519 if (param->isclosure) {
520 socket_type =
"NodeSocketShader";
521 data_type = BL::NodeSocket::type_SHADER;
523 else if (param->type.vecsemantics == TypeDesc::COLOR) {
524 socket_type =
"NodeSocketColor";
525 data_type = BL::NodeSocket::type_RGBA;
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];
533 else if (param->type.vecsemantics == TypeDesc::POINT ||
534 param->type.vecsemantics == TypeDesc::VECTOR ||
535 param->type.vecsemantics == TypeDesc::NORMAL)
537 socket_type =
"NodeSocketVector";
538 data_type = BL::NodeSocket::type_VECTOR;
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];
546 else if (param->type.aggregate == TypeDesc::SCALAR) {
547 if (param->type.basetype == TypeDesc::INT) {
549 socket_type =
"NodeSocketBool";
550 data_type = BL::NodeSocket::type_BOOLEAN;
551 if (param->validdefault) {
552 default_boolean = bool(param->idefault[0]);
556 socket_type =
"NodeSocketInt";
557 data_type = BL::NodeSocket::type_INT;
558 if (param->validdefault)
559 default_int = param->idefault[0];
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];
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();
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) {
587 b_node.outputs.remove(b_data, b_sock);
591 if (b_sock.name() != param_label) {
592 b_sock.name(param_label.string());
594 used_sockets.insert(b_sock.ptr.data);
595 found_existing =
true;
602 for (BL::NodeSocket &b_sock : b_node.inputs) {
603 if (b_sock.identifier() == param->name) {
604 if (b_sock.bl_idname() != socket_type) {
606 b_node.inputs.remove(b_data, b_sock);
610 if (b_sock.name() != param_label) {
611 b_sock.name(param_label.string());
613 if (b_sock.hide_value() != hide_value) {
614 b_sock.hide_value(hide_value);
616 used_sockets.insert(b_sock.ptr.data);
617 found_existing =
true;
624 if (!found_existing) {
626 BL::NodeSocket b_sock =
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);
634 if (data_type == BL::NodeSocket::type_VALUE) {
635 set_float(b_sock.ptr,
"default_value", default_float);
637 else if (data_type == BL::NodeSocket::type_INT) {
638 set_int(b_sock.ptr,
"default_value", default_int);
640 else if (data_type == BL::NodeSocket::type_RGBA) {
641 set_float4(b_sock.ptr,
"default_value", default_float4);
643 else if (data_type == BL::NodeSocket::type_VECTOR) {
646 else if (data_type == BL::NodeSocket::type_STRING) {
647 set_string(b_sock.ptr,
"default_value", default_string);
649 else if (data_type == BL::NodeSocket::type_BOOLEAN) {
650 set_boolean(b_sock.ptr,
"default_value", default_boolean);
653 b_sock.hide_value(hide_value);
655 used_sockets.insert(b_sock.ptr.data);
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);
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);
685static PyObject *osl_compile_func(PyObject * , PyObject *args)
687 const char *inputfile =
NULL, *outputfile =
NULL;
689 if (!PyArg_ParseTuple(args,
"ss", &inputfile, &outputfile))
693 if (!OSLShaderManager::osl_compile(inputfile, outputfile))
703 return pyunicode_from_string(system_info.c_str());
708 if (PyUnicode_Check(pyfilepaths)) {
709 const char *filepath = PyUnicode_AsUTF8(pyfilepaths);
710 filepaths.push_back(filepath);
714 PyObject *sequence = PySequence_Fast(pyfilepaths,
715 "File paths must be a string or sequence of strings");
716 if (sequence ==
NULL) {
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.");
728 filepaths.push_back(filepath);
735static PyObject *
denoise_func(PyObject * , PyObject *args, PyObject *keywords)
738 "preferences",
"scene",
"view_layer",
"input",
"output",
NULL};
739 PyObject *pypreferences, *pyscene, *pyviewlayer;
740 PyObject *pyinput, *pyoutput =
NULL;
742 if (!PyArg_ParseTupleAndKeywords(args,
757 NULL, &RNA_Preferences, (
void *)PyLong_AsVoidPtr(pypreferences));
758 BL::Preferences b_preferences(preferencesptr);
761 BL::Scene b_scene(sceneptr);
765 b_preferences, b_scene,
true,
true, preferences_device);
769 (
ID *)PyLong_AsVoidPtr(pyscene), &RNA_ViewLayer, PyLong_AsVoidPtr(pyviewlayer));
770 BL::ViewLayer b_view_layer(viewlayerptr);
773 b_scene, b_view_layer,
true, preferences_device);
793 PyErr_SetString(PyExc_ValueError,
"No input file paths specified.");
796 if (input.size() != output.size()) {
797 PyErr_SetString(PyExc_ValueError,
"Number of input and output file paths does not match.");
806 denoiser.
input = input;
810 if (!denoiser.
run()) {
811 PyErr_SetString(PyExc_ValueError, denoiser.
error.c_str());
818static PyObject *
merge_func(PyObject * , PyObject *args, PyObject *keywords)
821 PyObject *pyinput, *pyoutput =
NULL;
823 if (!PyArg_ParseTupleAndKeywords(
824 args, keywords,
"OO", (
char **)
keyword_list, &pyinput, &pyoutput))
836 if (!PyUnicode_Check(pyoutput)) {
837 PyErr_SetString(PyExc_ValueError,
"Output must be a string.");
840 string output = PyUnicode_AsUTF8(pyoutput);
844 merger.
input = input;
848 PyErr_SetString(PyExc_ValueError, merger.
error.c_str());
858 if (!PyArg_ParseTuple(args,
"O", &pyscene)) {
863 BL::Scene b_scene(sceneptr);
865 debug_flags_sync_from_scene(b_scene);
867 debug_flags_set =
true;
875 if (debug_flags_set) {
876 debug_flags_set =
false;
890 bool has_cuda =
false, has_optix =
false, has_hip =
false, has_metal =
false, has_oneapi =
false,
892 foreach (
DeviceType device_type, device_types) {
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));
912 PyObject *override_string = PyObject_Str(arg);
913 string override = PyUnicode_AsUTF8(override_string);
914 Py_DECREF(override_string);
916 bool include_cpu =
false;
917 const string cpu_suffix =
"+CPU";
920 override =
override.substr(0,
override.
length() - cpu_suffix.length());
923 if (
override ==
"CPU") {
926 else if (
override ==
"CUDA") {
929 else if (
override ==
"OPTIX") {
932 else if (
override ==
"HIP") {
935 else if (
override ==
"METAL") {
938 else if (
override ==
"ONEAPI") {
942 printf(
"\nError: %s is not a valid Cycles device.\n",
override.c_str());
967 {
"osl_update_node", osl_update_node_func, METH_VARARGS,
""},
968 {
"osl_compile", osl_compile_func, METH_VARARGS,
""},
974 {
"denoise", (PyCFunction)
denoise_func, METH_VARARGS | METH_KEYWORDS,
""},
975 {
"merge", (PyCFunction)
merge_func, METH_VARARGS | METH_KEYWORDS,
""},
992 PyModuleDef_HEAD_INIT,
994 "Blender cycles render integration",
1007 PyObject *
mod = PyModule_Create(&ccl::module);
1015 int curversion = OSL_LIBRARY_VERSION_CODE;
1016 PyModule_AddObjectRef(
mod,
"with_osl", Py_True);
1020 Py_BuildValue(
"(iii)", curversion / 10000, (curversion / 100) % 100, curversion % 100));
1023 "osl_version_string",
1024 PyUnicode_FromFormat(
1025 "%2d, %2d, %2d", curversion / 10000, (curversion / 100) % 100, curversion % 100));
1027 PyModule_AddObjectRef(
mod,
"with_osl", Py_False);
1028 PyModule_AddStringConstant(
mod,
"osl_version",
"unknown");
1029 PyModule_AddStringConstant(
mod,
"osl_version_string",
"unknown");
1032 if (ccl::guiding_supported()) {
1033 PyModule_AddObjectRef(
mod,
"with_path_guiding", Py_True);
1036 PyModule_AddObjectRef(
mod,
"with_path_guiding", Py_False);
1040 PyModule_AddObjectRef(
mod,
"with_embree", Py_True);
1042 PyModule_AddObjectRef(
mod,
"with_embree", Py_False);
1045#ifdef WITH_EMBREE_GPU
1046 PyModule_AddObjectRef(
mod,
"with_embree_gpu", Py_True);
1048 PyModule_AddObjectRef(
mod,
"with_embree_gpu", Py_False);
1051 if (ccl::openimagedenoise_supported()) {
1052 PyModule_AddObjectRef(
mod,
"with_openimagedenoise", Py_True);
1055 PyModule_AddObjectRef(
mod,
"with_openimagedenoise", Py_False);
1058#ifdef WITH_CYCLES_DEBUG
1059 PyModule_AddObjectRef(
mod,
"with_debug", Py_True);
1061 PyModule_AddObjectRef(
mod,
"with_debug", Py_False);
void GPU_viewport_size_get_i(int coords[4])
DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scene, bool background, bool preview, DeviceInfo &preferences_device)
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
static bool print_render_stats
void synchronize(BL::Depsgraph &b_depsgraph)
void * python_thread_state
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)
void render_frame_finish()
static DeviceTypeMask device_override
static DenoiseParams get_denoise_params(BL::Scene &b_scene, BL::ViewLayer &b_view_layer, bool background, const DeviceInfo &denoise_device)
DenoiserTypeMask denoisers
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)
void append(const uint8_t *data, int size)
static void free_memory()
static void free_memory()
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()
@ DENOISER_OPENIMAGEDENOISE
#define CCL_NAMESPACE_END
#define DEVICE_MASK(type)
static KeywordTokenDef keyword_list[]
static void debug_flags_reset()
static void debug_flags_sync_from_scene(BL::Scene b_scene)
void * pylong_as_voidptr_typesafe(PyObject *object)
PyObject * pyunicode_from_string(const char *str)
VecBase< float, 4 > float4
CCL_NAMESPACE_BEGIN typedef KernelBVHLayout BVHLayout
void path_init(const string &path, const string &user_path)
static PyObject * init_func(PyObject *, PyObject *args)
static PyObject * merge_func(PyObject *, PyObject *args, PyObject *keywords)
static PyObject * sync_func(PyObject *, PyObject *args)
static PyObject * denoise_func(PyObject *, PyObject *args, PyObject *keywords)
void python_thread_state_restore(void **python_thread_state)
static PyObject * system_info_func(PyObject *, PyObject *)
static PyObject * set_device_override_func(PyObject *, PyObject *arg)
static PyObject * free_func(PyObject *, PyObject *value)
CCL_NAMESPACE_END void * CCL_python_module_init()
static struct PyModuleDef module
static bool image_parse_filepaths(PyObject *pyfilepaths, vector< string > &filepaths)
static PyMethodDef methods[]
static PyObject * available_devices_func(PyObject *, PyObject *args)
static PyObject * bake_func(PyObject *, PyObject *args)
static PyObject * reset_func(PyObject *, PyObject *args)
static PyObject * view_draw_func(PyObject *, PyObject *args)
static PyObject * enable_print_stats_func(PyObject *, PyObject *)
static PyObject * debug_flags_reset_func(PyObject *, PyObject *)
static PyObject * render_func(PyObject *, PyObject *args)
static PyObject * debug_flags_update_func(PyObject *, PyObject *args)
void python_thread_state_save(void **python_thread_state)
static PyObject * draw_func(PyObject *, PyObject *args)
static PyObject * get_device_types_func(PyObject *, PyObject *)
static const char * PyC_UnicodeAsBytes(PyObject *py_str, PyObject **coerce)
static PyObject * render_frame_finish_func(PyObject *, PyObject *args)
static PyObject * create_func(PyObject *, PyObject *args)
static PyObject * exit_func(PyObject *, PyObject *)
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)
bool string_endswith(const string_view s, const string_view end)
ccl_device_inline int mod(int x, int m)
ccl_device_inline float3 float4_to_float3(const float4 a)