Changeset View
Changeset View
Standalone View
Standalone View
source/blender/python/intern/gpu_shader_node.c
- This file was added.
| /* | |||||
| * ***** BEGIN GPL LICENSE BLOCK ***** | |||||
| * | |||||
| * This program is free software; you can redistribute it and/or | |||||
| * modify it under the terms of the GNU General Public License | |||||
| * as published by the Free Software Foundation; either version 2 | |||||
| * of the License, or (at your option) any later version. | |||||
| * | |||||
| * This program is distributed in the hope that it will be useful, | |||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
| * GNU General Public License for more details. | |||||
| * | |||||
| * You should have received a copy of the GNU General Public License | |||||
| * along with this program; if not, write to the Free Software Foundation, | |||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||||
| * | |||||
| * Copyright 2017, Blender Foundation. | |||||
| * | |||||
| * ***** END GPL LICENSE BLOCK ***** | |||||
| */ | |||||
| /** \file blender/python/intern/gpu_builtin.c | |||||
| * \ingroup pythonintern | |||||
| * | |||||
| * This file defines the builtin identifiers for use in GLSL shaders. | |||||
| */ | |||||
| #include <Python.h> | |||||
| #include "DNA_image_types.h" | |||||
| #include "BLI_utildefines.h" | |||||
| #include "GPU_material.h" | |||||
| #include "gpu.h" | |||||
| #include "../generic/py_capi_utils.h" | |||||
| #include "mathutils/mathutils.h" | |||||
| #include "BKE_node.h" | |||||
| #include "BPY_extern.h" | |||||
| #include "RNA_access.h" | |||||
| PyDoc_STRVAR(M_gpu_shader_node_doc, | |||||
| "This module provides access to the GLSL shader node functionalities." | |||||
| ); | |||||
| typedef struct { | |||||
| PyObject_HEAD | |||||
| } BPy_GPUShaderNode; | |||||
| typedef struct GPULink { | |||||
| PyObject_VAR_HEAD | |||||
| GPULinkDesc desc; | |||||
| } GPULink; | |||||
| PyDoc_STRVAR(pygpu_add_engine_compatibility_doc, | |||||
| "add_engine_compatibility()\n" | |||||
| "\n" | |||||
| " Return call result\n" | |||||
| "\n" | |||||
| " :param engine: render engine name\n" | |||||
| " :type engine: `str`\n" | |||||
| " :param node: node name\n" | |||||
| " :type node: `str`\n" | |||||
| " :param param: additional parameter\n" | |||||
| " :type param: tuple (gpufunc, library) or str,\n" | |||||
| " where gpufunc - function to replace corresponding shader node function\n" | |||||
| " library - glsl text with str type\n" | |||||
| " :return: call result\n" | |||||
| " :rtype: :class: int\n" | |||||
| ); | |||||
| static PyObject *pygpu_add_engine_compatibility(PyObject *UNUSED(self), PyObject *args, PyObject *kwds); | |||||
| PyDoc_STRVAR(pygpu_remove_engine_compatibility_doc, | |||||
| "remove_engine_compatibility()\n" | |||||
| "\n" | |||||
| " Return call result\n" | |||||
| "\n" | |||||
| " :param engine: render engine name.\n" | |||||
| " :type engine: str\n" | |||||
| " :return: call result\n" | |||||
| " :rtype: :class: int\n" | |||||
| ); | |||||
| static PyObject * pygpu_remove_engine_compatibility(PyObject *UNUSED(self), PyObject *args, PyObject *kwds); | |||||
| PyDoc_STRVAR(pygpu_gpu_link_doc, | |||||
| "GPU_link(glsl_fnc, args)\n" | |||||
| "\n" | |||||
| " Return true if succeed.\n" | |||||
| "\n" | |||||
| " :param glsl_fnc: name of GLSL function from library\n" | |||||
| " :type glsl_fnc: `str`\n" | |||||
| " :param args: list of GPULinks\n" | |||||
| " :type args: `list`\n" | |||||
| " :return: result of invocation\n" | |||||
| " :rtype: :class:`boolean`\n" | |||||
| ); | |||||
| static PyObject *pygpu_gpu_link(PyObject *UNUSED(self), PyObject *args, PyObject *kwds); | |||||
| PyDoc_STRVAR(pygpu_gpu_link_output_doc, | |||||
| "GPU_link_output(link)\n" | |||||
| "\n" | |||||
| " Return true if successed\n" | |||||
| "\n" | |||||
| " :param link: output link\n" | |||||
| " :type link: :class: `GPULink`\n" | |||||
| " :return: result of invocation\n" | |||||
| " :rtype: :class:`boolean`\n" | |||||
| ); | |||||
| static PyObject *pygpu_gpu_link_output(PyObject *UNUSED(self), PyObject *args, PyObject *kwds); | |||||
| PyDoc_STRVAR(pygpu_gpu_get_shader_node_classes_doc, | |||||
| "get_shader_node_classes()\n" | |||||
| "\n" | |||||
| " Return tuple of all registered classes derrived from ShaderNodes.\n" | |||||
| "\n" | |||||
| " :return: tuple of all registered classes derrived from ShaderNodes\n" | |||||
| " :rtype: :class:`tuple`\n" | |||||
| ); | |||||
| static PyObject *pygpu_get_shader_node_classes(PyObject *UNUSED(self), PyObject *args, PyObject *kwds); | |||||
| static struct PyMethodDef BPy_GPU_shader_node_methods[] = { | |||||
| {"GPU_link", (PyCFunction)pygpu_gpu_link, METH_VARARGS | METH_KEYWORDS, pygpu_gpu_link_doc}, | |||||
| {"GPU_link_output", (PyCFunction)pygpu_gpu_link_output, METH_VARARGS | METH_KEYWORDS, pygpu_gpu_link_output_doc}, | |||||
| {"get_shader_node_classes", (PyCFunction)pygpu_get_shader_node_classes, METH_NOARGS, pygpu_gpu_get_shader_node_classes_doc}, | |||||
| {"add_engine_compatibility", (PyCFunction)pygpu_add_engine_compatibility, METH_VARARGS | METH_KEYWORDS, pygpu_add_engine_compatibility_doc}, | |||||
| {"remove_engine_compatibility", (PyCFunction)pygpu_remove_engine_compatibility, METH_VARARGS | METH_KEYWORDS, pygpu_remove_engine_compatibility_doc}, | |||||
| {NULL, NULL, 0, NULL} | |||||
| }; | |||||
| PyDoc_STRVAR(gpulink_doc, | |||||
| ".. class:: GPULink()\n" | |||||
| "\n" | |||||
| " This object adds possibility to create graphs for shader generation in Blender.\n" | |||||
| "\n" | |||||
| ); | |||||
| static PyObject *gpulink_str(GPUNodeLink *self) | |||||
| { | |||||
| return PyUnicode_FromFormat("<GPUNodeLink at %p>", self); | |||||
| } | |||||
| static PyObject *gpulink_new(PyTypeObject *type, PyObject *args, PyObject *kwds); | |||||
| PyTypeObject gpulink_Type = { | |||||
| PyVarObject_HEAD_INIT(NULL, 0) | |||||
| /* For printing, in format "<module>.<name>" */ | |||||
| "GPULink", /* char *tp_name; */ | |||||
| sizeof(GPULink), /* int tp_basicsize; */ | |||||
| 0, /* tp_itemsize; For allocation */ | |||||
| /* Methods to implement standard operations */ | |||||
| NULL, /* destructor tp_dealloc; */ | |||||
| NULL, /* printfunc tp_print; */ | |||||
| NULL, /* getattrfunc tp_getattr; */ | |||||
| NULL, /* setattrfunc tp_setattr; */ | |||||
| NULL, /* cmpfunc tp_compare; */ | |||||
| (reprfunc)gpulink_str, /* reprfunc tp_repr; */ | |||||
| /* Method suites for standard classes */ | |||||
| NULL, /* PyNumberMethods *tp_as_number; */ | |||||
| NULL, /* PySequenceMethods *tp_as_sequence; */ | |||||
| NULL, /* PyMappingMethods *tp_as_mapping; */ | |||||
| /* More standard operations (here for binary compatibility) */ | |||||
| NULL, /* hashfunc tp_hash; */ | |||||
| NULL, /* ternaryfunc tp_call; */ | |||||
| NULL, /* reprfunc tp_str; */ | |||||
| NULL, /* getattrofunc tp_getattro; */ | |||||
| NULL, /* setattrofunc tp_setattro; */ | |||||
| /* Functions to access object as input/output buffer */ | |||||
| NULL, /* PyBufferProcs *tp_as_buffer; */ | |||||
| /*** Flags to define presence of optional/expanded features ***/ | |||||
| Py_TPFLAGS_DEFAULT, /* tp_flags */ | |||||
| gpulink_doc, /* char *tp_doc; Documentation string */ | |||||
| /*** Assigned meaning in release 2.0 ***/ | |||||
| /* call function for all accessible objects */ | |||||
| NULL, /* tp_traverse */ | |||||
| /* delete references to contained objects */ | |||||
| NULL, /* tp_clear */ | |||||
| /*** Assigned meaning in release 2.1 ***/ | |||||
| /*** rich comparisons ***/ | |||||
| NULL, /* richcmpfunc tp_richcompare; */ | |||||
| /*** weak reference enabler ***/ | |||||
| 0, /* long tp_weaklistoffset; */ | |||||
| /*** Added in release 2.2 ***/ | |||||
| /* Iterators */ | |||||
| NULL, /* getiterfunc tp_iter; */ | |||||
| NULL, /* iternextfunc tp_iternext; */ | |||||
| /*** Attribute descriptor and subclassing stuff ***/ | |||||
| NULL, /* struct PyMethodDef *tp_methods; */ | |||||
| NULL, /* struct PyMemberDef *tp_members; */ | |||||
| NULL, /* struct PyGetSetDef *tp_getset; */ | |||||
| NULL, /* struct _typeobject *tp_base; */ | |||||
| NULL, /* PyObject *tp_dict; */ | |||||
| NULL, /* descrgetfunc tp_descr_get; */ | |||||
| NULL, /* descrsetfunc tp_descr_set; */ | |||||
| 0, /* long tp_dictoffset; */ | |||||
| NULL, /* initproc tp_init; */ | |||||
| NULL, /* allocfunc tp_alloc; */ | |||||
| gpulink_new, /* newfunc tp_new; */ | |||||
| /* Low-level free-memory routine */ | |||||
| NULL, /* freefunc tp_free; */ | |||||
| /* For PyObject_IS_GC */ | |||||
| NULL, /* inquiry tp_is_gc; */ | |||||
| NULL, /* PyObject *tp_bases; */ | |||||
| /* method resolution order */ | |||||
| NULL, /* PyObject *tp_mro; */ | |||||
| NULL, /* PyObject *tp_cache; */ | |||||
| NULL, /* PyObject *tp_subclasses; */ | |||||
| NULL, /* PyObject *tp_weaklist; */ | |||||
| NULL | |||||
| }; | |||||
| static PyObject *pygpu_get_shader_node_classes(PyObject *UNUSED(self), PyObject *UNUSED(args), PyObject *UNUSED(kwds)) | |||||
| { | |||||
| ListBase *lb = nodeGetShaderNodeTypes(); | |||||
| int len = BLI_listbase_count(lb); | |||||
| PyObject *ret; | |||||
| if (len) | |||||
| ret = PyTuple_New(len); | |||||
| else | |||||
| return PyTuple_New(0); | |||||
| int i = 0; | |||||
| if (len) | |||||
| for (LinkData * ld = lb->first; ld; ld = ld->next) { | |||||
| PyObject *t; | |||||
| if (((bNodeType *)ld->data)->ext.data) { | |||||
| t = ((bNodeType *)ld->data)->ext.data; | |||||
| PyTuple_SetItem(ret, i, t); | |||||
| i++; | |||||
| } | |||||
| else { | |||||
| PyObject *types = PyImport_ImportModule("bpy.types"); | |||||
| t = PyObject_GetAttrString(types, ((bNodeType *)ld->data)->idname); | |||||
| ((bNodeType *)ld->data)->ext.data = t; | |||||
| PyTuple_SetItem(ret, i, t); | |||||
| i++; | |||||
| } | |||||
| Py_INCREF(t); | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| static PyObject *pygpu_remove_engine_compatibility(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) | |||||
| { | |||||
| if (kwds && PyDict_Size(kwds)) { | |||||
| PyErr_SetString(PyExc_TypeError, | |||||
| "remove_engine_compatibility(): " | |||||
| "takes no keyword args"); | |||||
| return NULL; | |||||
| } | |||||
| char *engine_name = NULL; | |||||
| char *node_name = NULL; | |||||
| if (!PyArg_UnpackTuple(args, "ss:pygpu_remove_engine_compatibility", 2, 2, &engine_name, &node_name)) | |||||
| return NULL; | |||||
| if (0 == node_shader_compatibility_cache_remove(engine_name, node_name)) | |||||
| Py_RETURN_TRUE; | |||||
| return NULL; | |||||
| } | |||||
| static PyObject *pygpu_add_engine_compatibility(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) | |||||
| { | |||||
| if (kwds && PyDict_Size(kwds)) { | |||||
| PyErr_SetString(PyExc_TypeError, | |||||
| "add_engine_compatibility(): " | |||||
| "takes no keyword args"); | |||||
| return NULL; | |||||
| } | |||||
| char *engine_name = NULL; | |||||
| char *node_name = NULL; | |||||
| char *library = NULL; | |||||
| char *reuse_engine = NULL; | |||||
| PyObject *gpufunc = NULL; | |||||
| int argc = PyTuple_GET_SIZE(args); | |||||
| if (!(argc == 2 || argc == 3)) { | |||||
| PyErr_Format(PyExc_RuntimeError, | |||||
| "gpu.add_engine_compatibility(...) wrong number of arguments"); | |||||
| return NULL; | |||||
| } | |||||
| engine_name = _PyUnicode_AsString(PyTuple_GET_ITEM(args, 0)); | |||||
| node_name = _PyUnicode_AsString(PyTuple_GET_ITEM(args, 1)); | |||||
| if (argc == 3) { | |||||
| PyObject* arg3 = PyTuple_GET_ITEM(args, 2); | |||||
| if (PyTuple_Check(arg3)) { | |||||
| gpufunc = PyTuple_GET_ITEM(arg3, 0); | |||||
| PyObject *library_ob = PyTuple_GET_ITEM(arg3, 1); | |||||
| if (Py_None != library_ob) { | |||||
| if (!(library = _PyUnicode_AsString(library_ob))) { | |||||
| PyErr_Format(PyExc_RuntimeError, | |||||
| "gpu.add_engine_compatibility(...) wrong type of library arg"); | |||||
| return NULL; | |||||
| } | |||||
| } | |||||
| if (!PyCallable_Check(gpufunc)) { | |||||
| PyErr_Format(PyExc_RuntimeError, | |||||
| "gpu.add_engine_compatibility(...) wrong type of gpufunc argument"); | |||||
| return NULL; | |||||
| } | |||||
| } | |||||
| else if (!(reuse_engine = _PyUnicode_AsString(arg3))) { | |||||
| PyErr_Format(PyExc_RuntimeError, | |||||
| "gpu.add_engine_compatibility(...) wrong type of 3rd argument"); | |||||
| return NULL; | |||||
| } | |||||
| } | |||||
| bNodeType *nt = nodeTypeFind(node_name); | |||||
| if (!nt) | |||||
| return NULL; | |||||
| if (gpufunc) | |||||
| Py_INCREF(gpufunc); | |||||
| node_shader_compatibility_cache_add(engine_name, node_name, gpufunc, library, reuse_engine); | |||||
| Py_RETURN_TRUE; | |||||
| } | |||||
| static PyObject *pygpu_gpu_link(PyObject *UNUSED(self), PyObject *args, PyObject *UNUSED(kwds)) | |||||
| { | |||||
| PyObject *fnc_name; | |||||
| PyObject *fnc_args; | |||||
| int ret = 0; | |||||
| if (!PyArg_UnpackTuple(args, "so:GPU_link", 2, 2, &fnc_name, &fnc_args)) | |||||
| return NULL; | |||||
| GPUMaterial *mat = BPY_context_gpumat_get(); | |||||
| char * name = _PyUnicode_AsString(fnc_name); | |||||
| int len = PySequence_Size(fnc_args); | |||||
| GPULinkDesc **link_args = PyMem_Malloc(sizeof (GPULinkDesc *) * len); | |||||
| for (int i = 0; i < len; i++) { | |||||
| PyObject *item = PySequence_GetItem(fnc_args, i); | |||||
| if (!PyObject_TypeCheck(item, &gpulink_Type)) | |||||
| { | |||||
| PyErr_Format(PyExc_RuntimeError, | |||||
| "gpu.GPU_link(...) bad %d item of arguments", i); | |||||
| goto exit; | |||||
| } | |||||
| link_args[i] = &((GPULink *)item)->desc; | |||||
| } | |||||
| ret = GPU_link_py(mat, name, link_args, len); | |||||
| exit: | |||||
| PyMem_Free(link_args); | |||||
| if (ret) | |||||
| Py_RETURN_TRUE; | |||||
| else | |||||
| return NULL; | |||||
| } | |||||
| static PyObject *pygpu_gpu_link_output(PyObject *UNUSED(self), PyObject *args, PyObject *UNUSED(kwds)) | |||||
| { | |||||
| PyObject* link; | |||||
| if (!PyArg_UnpackTuple(args, "o:GPU_link", 1, 1, &link)) | |||||
| return NULL; | |||||
| if (!PyObject_TypeCheck(link, &gpulink_Type)) { | |||||
| PyErr_Format(PyExc_RuntimeError, | |||||
| "gpu.GPU_link_output(...) bad argument"); | |||||
| return NULL; | |||||
| } | |||||
| void *mat = BPY_context_gpumat_get(); | |||||
| GPULinkDesc* desc = &((GPULink *)link)->desc; | |||||
| GPUNodeLink * l; | |||||
| if (desc->type == LINK_REF) | |||||
| l = *((GPUNodeLink **)desc->link); | |||||
| else | |||||
| l = desc->link; | |||||
| GPU_material_output_link(mat, l); | |||||
| Py_RETURN_NONE; | |||||
| } | |||||
| static PyObject *create_gpu_link(void *link, GPULinkType type) | |||||
| { | |||||
| GPULink * ret = (GPULink *) _PyObject_GC_New(&(gpulink_Type)); | |||||
| ret->desc.link = link; | |||||
| ret->desc.type = type; | |||||
| return (PyObject *)ret; | |||||
| } | |||||
| static PyObject *create_gpu_uniform(float *ptr) { | |||||
| GPULink * ret = (GPULink *) _PyObject_GC_New(&(gpulink_Type)); | |||||
| ret->desc.link = GPU_uniform(ptr); | |||||
| return (PyObject *)ret; | |||||
| } | |||||
| static PyObject *create_gpulink_from_1_param(PyObject* obj) | |||||
| { | |||||
| GPUNodeLink *link; | |||||
| /* if GPUNodeStack */ | |||||
| GPUNodeStack *gns = PyC_RNA_AsPointer(obj, "GPUNodeStack"); | |||||
| if (gns) { | |||||
| return create_gpu_link(&gns->link, LINK_REF); | |||||
| } | |||||
| else { | |||||
| /* if GPUBuiltin */ | |||||
| PyErr_Clear(); | |||||
| int id = _PyLong_AsInt(obj); | |||||
| if (!(id == -1 && PyErr_Occurred())) { | |||||
| link = GPU_builtin(id); | |||||
| return create_gpu_link(link, LINK); | |||||
| } | |||||
| else { | |||||
| PyErr_Clear(); | |||||
| /* if Vector/Matrix */ | |||||
| if (VectorObject_Check(obj) || MatrixObject_Check(obj)) { | |||||
| return create_gpu_uniform(((BaseMathObject *)obj)->data); | |||||
| } | |||||
| } | |||||
| } | |||||
| PyErr_SetString(PyExc_TypeError, | |||||
| "gpu.GPULink(): " | |||||
| "Parameter type is not supported"); | |||||
| return NULL; | |||||
| } | |||||
| static PyObject *create_gpulink_from_2_param(PyObject* obj1, PyObject* obj2) | |||||
| { | |||||
| GPUNodeLink *link; | |||||
| /* gpu attribute */ | |||||
| int id = _PyLong_AsInt(obj1); | |||||
| if (!(id == -1 && PyErr_Occurred())) { | |||||
| char *name = _PyUnicode_AsString(obj2); | |||||
| link = GPU_attribute(id, name); | |||||
| return create_gpu_link(link, LINK); | |||||
| } else { | |||||
| PyErr_Clear(); | |||||
| bool isdata = false; | |||||
| Image *ima = PyC_RNA_AsPointer(obj1, "Image"); | |||||
| if (PyC_ParseBool(obj2, &isdata)) { | |||||
| link = GPU_image(ima, NULL, isdata); | |||||
| return create_gpu_link(link, LINK); | |||||
| } | |||||
| } | |||||
| PyErr_SetString(PyExc_TypeError, | |||||
| "gpu.GPULink(): " | |||||
| "Parameter type is not supported"); | |||||
| return NULL; | |||||
| } | |||||
| static PyObject *gpulink_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |||||
| { | |||||
| if (kwds && PyDict_Size(kwds)) { | |||||
| PyErr_SetString(PyExc_TypeError, | |||||
| "GPULink(): " | |||||
| "takes no keyword args"); | |||||
| return NULL; | |||||
| } | |||||
| GPULink *self; | |||||
| switch (PyTuple_GET_SIZE(args)) { | |||||
| case 0: | |||||
| self = (GPULink *)((type ? (type)->tp_alloc(type, 0) : _PyObject_GC_New(&(gpulink_Type)))); | |||||
| if (self) { | |||||
| self->desc.link = NULL; | |||||
| self->desc.type = LINK; | |||||
| } | |||||
| break; | |||||
| case 1: | |||||
| self = (GPULink *)create_gpulink_from_1_param(PyTuple_GET_ITEM(args, 0)); | |||||
| break; | |||||
| case 2: | |||||
| self = (GPULink *)create_gpulink_from_2_param(PyTuple_GET_ITEM(args, 0), PyTuple_GET_ITEM(args, 1)); | |||||
| break; | |||||
| default: | |||||
| PyErr_SetString(PyExc_TypeError, | |||||
| "gpu.GPULink(): " | |||||
| "Too many arguments"); | |||||
| return NULL; | |||||
| } | |||||
| return (PyObject *) self; | |||||
| } | |||||
| static struct PyModuleDef gpu_shader_node_module = { | |||||
| PyModuleDef_HEAD_INIT, | |||||
| "shader_node", /* name of module */ | |||||
| M_gpu_shader_node_doc, /* module documentation */ | |||||
| -1, /* size of per-interpreter state of the module, */ | |||||
| /* or -1 if the module keeps state in global variables. */ | |||||
| BPy_GPU_shader_node_methods, /* methods */ | |||||
| NULL, NULL, NULL, NULL | |||||
| }; | |||||
| PyObject *BPyInit_gpu_shader_node(void) | |||||
| { | |||||
| PyObject *module; | |||||
| PyObject *submodule; | |||||
| PyObject *sys_modules = PyThreadState_GET()->interp->modules; | |||||
| module = PyModule_Create(&gpu_shader_node_module); | |||||
| if (module == NULL) | |||||
| return NULL; | |||||
| /* gpu.shader_node.GPULink */ | |||||
| PyType_Ready(&gpulink_Type); | |||||
| PyModule_AddObject(module, gpulink_Type.tp_name, (PyObject *)&gpulink_Type); | |||||
| /* gpu.shader_node.builtins */ | |||||
| PyModule_AddObject(module, "builtins", (submodule = BPyInit_gpu_builtins())); | |||||
| PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule); | |||||
| Py_INCREF(submodule); | |||||
| /* gpu.shader_node.custom_data */ | |||||
| PyModule_AddObject(module, "custom_data", (submodule = BPyInit_gpu_custom_data())); | |||||
| PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule); | |||||
| Py_INCREF(submodule); | |||||
| return module; | |||||
| } | |||||