34df4cb1a4b7219485dedfdceb5176e559e61291
[blender.git] / source / blender / python / gpu / gpu_py_vertex_format.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup bpygpu
19  *
20  * - Use ``bpygpu_`` for local API.
21  * - Use ``BPyGPU`` for public API.
22  */
23
24 #include <Python.h>
25
26 #include "BLI_math.h"
27
28 #include "MEM_guardedalloc.h"
29
30 #include "../generic/py_capi_utils.h"
31 #include "../generic/python_utildefines.h"
32
33 #include "gpu_py_vertex_format.h" /* own include */
34
35 #ifdef __BIG_ENDIAN__
36 /* big endian */
37 #  define MAKE_ID2(c, d) ((c) << 8 | (d))
38 #  define MAKE_ID3(a, b, c) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8)
39 #  define MAKE_ID4(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
40 #else
41 /* little endian  */
42 #  define MAKE_ID2(c, d) ((d) << 8 | (c))
43 #  define MAKE_ID3(a, b, c) ((int)(c) << 16 | (b) << 8 | (a))
44 #  define MAKE_ID4(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
45 #endif
46
47 /* -------------------------------------------------------------------- */
48 /** \name Enum Conversion
49  *
50  * Use with PyArg_ParseTuple's "O&" formatting.
51  * \{ */
52
53 static int bpygpu_parse_component_type(const char *str, int length)
54 {
55   if (length == 2) {
56     switch (*((ushort *)str)) {
57       case MAKE_ID2('I', '8'):
58         return GPU_COMP_I8;
59       case MAKE_ID2('U', '8'):
60         return GPU_COMP_U8;
61       default:
62         break;
63     }
64   }
65   else if (length == 3) {
66     switch (*((uint *)str)) {
67       case MAKE_ID3('I', '1', '6'):
68         return GPU_COMP_I16;
69       case MAKE_ID3('U', '1', '6'):
70         return GPU_COMP_U16;
71       case MAKE_ID3('I', '3', '2'):
72         return GPU_COMP_I32;
73       case MAKE_ID3('U', '3', '2'):
74         return GPU_COMP_U32;
75       case MAKE_ID3('F', '3', '2'):
76         return GPU_COMP_F32;
77       case MAKE_ID3('I', '1', '0'):
78         return GPU_COMP_I10;
79       default:
80         break;
81     }
82   }
83   return -1;
84 }
85
86 static int bpygpu_parse_fetch_mode(const char *str, int length)
87 {
88 #define MATCH_ID(id) \
89   if (length == strlen(STRINGIFY(id))) { \
90     if (STREQ(str, STRINGIFY(id))) { \
91       return GPU_FETCH_##id; \
92     } \
93   } \
94   ((void)0)
95
96   MATCH_ID(FLOAT);
97   MATCH_ID(INT);
98   MATCH_ID(INT_TO_FLOAT_UNIT);
99   MATCH_ID(INT_TO_FLOAT);
100 #undef MATCH_ID
101
102   return -1;
103 }
104
105 static int bpygpu_ParseVertCompType(PyObject *o, void *p)
106 {
107   Py_ssize_t length;
108   const char *str = _PyUnicode_AsStringAndSize(o, &length);
109
110   if (str == NULL) {
111     PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
112     return 0;
113   }
114
115   int comp_type = bpygpu_parse_component_type(str, length);
116   if (comp_type == -1) {
117     PyErr_Format(PyExc_ValueError, "unkown component type: '%s", str);
118     return 0;
119   }
120
121   *((GPUVertCompType *)p) = comp_type;
122   return 1;
123 }
124
125 static int bpygpu_ParseVertFetchMode(PyObject *o, void *p)
126 {
127   Py_ssize_t length;
128   const char *str = _PyUnicode_AsStringAndSize(o, &length);
129
130   if (str == NULL) {
131     PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
132     return 0;
133   }
134
135   int fetch_mode = bpygpu_parse_fetch_mode(str, length);
136   if (fetch_mode == -1) {
137     PyErr_Format(PyExc_ValueError, "unknown type literal: '%s'", str);
138     return 0;
139   }
140
141   (*(GPUVertFetchMode *)p) = fetch_mode;
142   return 1;
143 }
144
145 /** \} */
146
147 /* -------------------------------------------------------------------- */
148 /** \name VertFormat Type
149  * \{ */
150
151 static PyObject *bpygpu_VertFormat_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
152 {
153   if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) {
154     PyErr_SetString(PyExc_ValueError, "This function takes no arguments");
155     return NULL;
156   }
157   return BPyGPUVertFormat_CreatePyObject(NULL);
158 }
159
160 PyDoc_STRVAR(
161     bpygpu_VertFormat_attr_add_doc,
162     ".. method:: attr_add(id, comp_type, len, fetch_mode)\n"
163     "\n"
164     "   Add a new attribute to the format.\n"
165     "\n"
166     "   :param id: Name the attribute. Often `position`, `normal`, ...\n"
167     "   :type id: str\n"
168     "   :param comp_type: The data type that will be used store the value in memory.\n"
169     "      Possible values are `I8`, `U8`, `I16`, `U16`, `I32`, `U32`, `F32` and `I10`.\n"
170     "   :type comp_type: `str`\n"
171     "   :param len: How many individual values the attribute consists of (e.g. 2 for uv "
172     "coordinates).\n"
173     "   :type len: int\n"
174     "   :param fetch_mode: How values from memory will be converted when used in the shader.\n"
175     "      This is mainly useful for memory optimizations when you want to store values with "
176     "reduced precision.\n"
177     "      E.g. you can store a float in only 1 byte but it will be converted to a normal 4 byte "
178     "float when used.\n"
179     "      Possible values are `FLOAT`, `INT`, `INT_TO_FLOAT_UNIT` and `INT_TO_FLOAT`.\n"
180     "   :type fetch_mode: `str`\n");
181 static PyObject *bpygpu_VertFormat_attr_add(BPyGPUVertFormat *self, PyObject *args, PyObject *kwds)
182 {
183   struct {
184     const char *id;
185     GPUVertCompType comp_type;
186     uint len;
187     GPUVertFetchMode fetch_mode;
188   } params;
189
190   if (self->fmt.attr_len == GPU_VERT_ATTR_MAX_LEN) {
191     PyErr_SetString(PyExc_ValueError, "Maxumum attr reached " STRINGIFY(GPU_VERT_ATTR_MAX_LEN));
192     return NULL;
193   }
194
195   static const char *_keywords[] = {"id", "comp_type", "len", "fetch_mode", NULL};
196   static _PyArg_Parser _parser = {"$sO&IO&:attr_add", _keywords, 0};
197   if (!_PyArg_ParseTupleAndKeywordsFast(args,
198                                         kwds,
199                                         &_parser,
200                                         &params.id,
201                                         bpygpu_ParseVertCompType,
202                                         &params.comp_type,
203                                         &params.len,
204                                         bpygpu_ParseVertFetchMode,
205                                         &params.fetch_mode)) {
206     return NULL;
207   }
208
209   uint attr_id = GPU_vertformat_attr_add(
210       &self->fmt, params.id, params.comp_type, params.len, params.fetch_mode);
211   return PyLong_FromLong(attr_id);
212 }
213
214 static struct PyMethodDef bpygpu_VertFormat_methods[] = {
215     {"attr_add",
216      (PyCFunction)bpygpu_VertFormat_attr_add,
217      METH_VARARGS | METH_KEYWORDS,
218      bpygpu_VertFormat_attr_add_doc},
219     {NULL, NULL, 0, NULL},
220 };
221
222 static void bpygpu_VertFormat_dealloc(BPyGPUVertFormat *self)
223 {
224   Py_TYPE(self)->tp_free(self);
225 }
226
227 PyDoc_STRVAR(bpygpu_VertFormat_doc,
228              ".. class:: GPUVertFormat()\n"
229              "\n"
230              "   This object contains information about the structure of a vertex buffer.\n");
231 PyTypeObject BPyGPUVertFormat_Type = {
232     PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertFormat",
233     .tp_basicsize = sizeof(BPyGPUVertFormat),
234     .tp_dealloc = (destructor)bpygpu_VertFormat_dealloc,
235     .tp_flags = Py_TPFLAGS_DEFAULT,
236     .tp_doc = bpygpu_VertFormat_doc,
237     .tp_methods = bpygpu_VertFormat_methods,
238     .tp_new = bpygpu_VertFormat_new,
239 };
240
241 /** \} */
242
243 /* -------------------------------------------------------------------- */
244 /** \name Public API
245  * \{ */
246
247 PyObject *BPyGPUVertFormat_CreatePyObject(GPUVertFormat *fmt)
248 {
249   BPyGPUVertFormat *self;
250
251   self = PyObject_New(BPyGPUVertFormat, &BPyGPUVertFormat_Type);
252   if (fmt) {
253     self->fmt = *fmt;
254   }
255   else {
256     memset(&self->fmt, 0, sizeof(self->fmt));
257   }
258
259   return (PyObject *)self;
260 }
261
262 /** \} */