441 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			441 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * ***** 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 2015, Blender Foundation.
 | 
						|
 *
 | 
						|
 * ***** END GPL LICENSE BLOCK *****
 | 
						|
 */
 | 
						|
 | 
						|
/** \file blender/python/gpu/gpu_py_batch.c
 | 
						|
 *  \ingroup bpygpu
 | 
						|
 *
 | 
						|
 * This file defines the offscreen functionalities of the 'gpu' module
 | 
						|
 * used for off-screen OpenGL rendering.
 | 
						|
 *
 | 
						|
 * - Use ``bpygpu_`` for local API.
 | 
						|
 * - Use ``BPyGPU`` for public API.
 | 
						|
 */
 | 
						|
 | 
						|
#include <Python.h>
 | 
						|
 | 
						|
#include "MEM_guardedalloc.h"
 | 
						|
 | 
						|
#include "BLI_utildefines.h"
 | 
						|
 | 
						|
#include "BKE_global.h"
 | 
						|
#include "BKE_library.h"
 | 
						|
 | 
						|
#include "GPU_batch.h"
 | 
						|
 | 
						|
#include "../mathutils/mathutils.h"
 | 
						|
 | 
						|
#include "../generic/py_capi_utils.h"
 | 
						|
 | 
						|
#include "gpu_py_shader.h"
 | 
						|
#include "gpu_py_vertex_buffer.h"
 | 
						|
#include "gpu_py_batch.h" /* own include */
 | 
						|
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
 | 
						|
/** \name VertBatch Type
 | 
						|
 * \{ */
 | 
						|
 | 
						|
static int bpygpu_ParsePrimType(PyObject *o, void *p)
 | 
						|
{
 | 
						|
	Py_ssize_t mode_id_len;
 | 
						|
	const char *mode_id = _PyUnicode_AsStringAndSize(o, &mode_id_len);
 | 
						|
	if (mode_id == NULL) {
 | 
						|
		PyErr_Format(PyExc_ValueError,
 | 
						|
		             "expected a string, got %s",
 | 
						|
		             Py_TYPE(o)->tp_name);
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
#define MATCH_ID(id) \
 | 
						|
	if (mode_id_len == strlen(STRINGIFY(id))) { \
 | 
						|
		if (STREQ(mode_id, STRINGIFY(id))) { \
 | 
						|
			mode = GPU_PRIM_##id; \
 | 
						|
			goto success; \
 | 
						|
		} \
 | 
						|
	} ((void)0)
 | 
						|
 | 
						|
	GPUPrimType mode;
 | 
						|
	MATCH_ID(POINTS);
 | 
						|
	MATCH_ID(LINES);
 | 
						|
	MATCH_ID(TRIS);
 | 
						|
	MATCH_ID(LINE_STRIP);
 | 
						|
	MATCH_ID(LINE_LOOP);
 | 
						|
	MATCH_ID(TRI_STRIP);
 | 
						|
	MATCH_ID(TRI_FAN);
 | 
						|
	MATCH_ID(LINE_STRIP_ADJ);
 | 
						|
 | 
						|
#undef MATCH_ID
 | 
						|
	PyErr_Format(PyExc_ValueError,
 | 
						|
	             "unknown type literal: '%s'",
 | 
						|
	             mode_id);
 | 
						|
	return 0;
 | 
						|
 | 
						|
success:
 | 
						|
	(*(GPUPrimType *)p) = mode;
 | 
						|
	return 1;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *bpygpu_Batch_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
 | 
						|
{
 | 
						|
	const char * const keywords[] = {"type", "buf", NULL};
 | 
						|
 | 
						|
	struct {
 | 
						|
		GPUPrimType type_id;
 | 
						|
		BPyGPUVertBuf *py_buf;
 | 
						|
	} params;
 | 
						|
 | 
						|
	if (!PyArg_ParseTupleAndKeywords(
 | 
						|
	        args, kwds,
 | 
						|
	        "$O&O!:GPUBatch.__new__", (char **)keywords,
 | 
						|
	        bpygpu_ParsePrimType, ¶ms.type_id,
 | 
						|
	        &BPyGPUVertBuf_Type, ¶ms.py_buf))
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	GPUBatch *batch = GPU_batch_create(params.type_id, params.py_buf->buf, NULL);
 | 
						|
	BPyGPUBatch *ret = (BPyGPUBatch *)BPyGPUBatch_CreatePyObject(batch);
 | 
						|
 | 
						|
#ifdef USE_GPU_PY_REFERENCES
 | 
						|
	ret->references = PyList_New(1);
 | 
						|
	PyList_SET_ITEM(ret->references, 0, (PyObject *)params.py_buf);
 | 
						|
	Py_INCREF(params.py_buf);
 | 
						|
	PyObject_GC_Track(ret);
 | 
						|
#endif
 | 
						|
 | 
						|
	return (PyObject *)ret;
 | 
						|
}
 | 
						|
 | 
						|
PyDoc_STRVAR(bpygpu_VertBatch_vertbuf_add_doc,
 | 
						|
"TODO"
 | 
						|
);
 | 
						|
static PyObject *bpygpu_VertBatch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_buf)
 | 
						|
{
 | 
						|
	if (!BPyGPUVertBuf_Check(py_buf)) {
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "Expected a GPUVertBuf, got %s",
 | 
						|
		             Py_TYPE(py_buf)->tp_name);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (self->batch->verts[0]->vertex_len != py_buf->buf->vertex_len) {
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "Expected %d length, got %d",
 | 
						|
		             self->batch->verts[0]->vertex_len, py_buf->buf->vertex_len);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
#ifdef USE_GPU_PY_REFERENCES
 | 
						|
	/* Hold user */
 | 
						|
	PyList_Append(self->references, (PyObject *)py_buf);
 | 
						|
#endif
 | 
						|
 | 
						|
	GPU_batch_vertbuf_add(self->batch, py_buf->buf);
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
PyDoc_STRVAR(bpygpu_VertBatch_program_set_doc,
 | 
						|
"TODO"
 | 
						|
);
 | 
						|
static PyObject *bpygpu_VertBatch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader)
 | 
						|
{
 | 
						|
	if (!BPyGPUShader_Check(py_shader)) {
 | 
						|
		PyErr_Format(PyExc_TypeError,
 | 
						|
		             "Expected a GPUShader, got %s",
 | 
						|
		             Py_TYPE(py_shader)->tp_name);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	GPUShader *shader = py_shader->shader;
 | 
						|
	GPU_batch_program_set(self->batch,
 | 
						|
	        GPU_shader_get_program(shader),
 | 
						|
	        GPU_shader_get_interface(shader));
 | 
						|
 | 
						|
#ifdef USE_GPU_PY_REFERENCES
 | 
						|
	/* Hold user */
 | 
						|
	PyList_Append(self->references, (PyObject *)py_shader);
 | 
						|
#endif
 | 
						|
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
PyDoc_STRVAR(bpygpu_VertBatch_program_set_builtin_doc,
 | 
						|
"TODO"
 | 
						|
);
 | 
						|
static PyObject *bpygpu_VertBatch_program_set_builtin(BPyGPUBatch *self, PyObject *args, PyObject *kwds)
 | 
						|
{
 | 
						|
	static const char *kwlist[] = {"id", NULL};
 | 
						|
 | 
						|
	struct {
 | 
						|
		const char *shader;
 | 
						|
	} params;
 | 
						|
 | 
						|
	if (!PyArg_ParseTupleAndKeywords(
 | 
						|
	        args, kwds, "s:program_set_builtin", (char **)kwlist,
 | 
						|
	        ¶ms.shader))
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	GPUBuiltinShader shader;
 | 
						|
 | 
						|
#define MATCH_ID(id) \
 | 
						|
	if (STREQ(params.shader, STRINGIFY(id))) { \
 | 
						|
		shader = GPU_SHADER_##id; \
 | 
						|
		goto success; \
 | 
						|
	} ((void)0)
 | 
						|
 | 
						|
	MATCH_ID(2D_FLAT_COLOR);
 | 
						|
	MATCH_ID(2D_SMOOTH_COLOR);
 | 
						|
	MATCH_ID(2D_UNIFORM_COLOR);
 | 
						|
 | 
						|
	MATCH_ID(3D_FLAT_COLOR);
 | 
						|
	MATCH_ID(3D_SMOOTH_COLOR);
 | 
						|
	MATCH_ID(3D_UNIFORM_COLOR);
 | 
						|
 | 
						|
#undef MATCH_ID
 | 
						|
 | 
						|
	PyErr_SetString(PyExc_ValueError,
 | 
						|
	                "shader name not known");
 | 
						|
	return NULL;
 | 
						|
 | 
						|
success:
 | 
						|
	GPU_batch_program_set_builtin(self->batch, shader);
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *bpygpu_VertBatch_uniform_bool(BPyGPUBatch *self, PyObject *args)
 | 
						|
{
 | 
						|
	struct {
 | 
						|
		const char *id;
 | 
						|
		bool values[1];
 | 
						|
	} params;
 | 
						|
 | 
						|
	if (!PyArg_ParseTuple(
 | 
						|
	        args, "sO&:uniform_bool",
 | 
						|
	        ¶ms.id,
 | 
						|
	        PyC_ParseBool, ¶ms.values[0]))
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	GPU_batch_uniform_1b(self->batch, params.id, params.values[0]);
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *bpygpu_VertBatch_uniform_i32(BPyGPUBatch *self, PyObject *args)
 | 
						|
{
 | 
						|
	struct {
 | 
						|
		const char *id;
 | 
						|
		int values[1];
 | 
						|
	} params;
 | 
						|
 | 
						|
	if (!PyArg_ParseTuple(
 | 
						|
	        args, "si:uniform_i32",
 | 
						|
	        ¶ms.id,
 | 
						|
	        ¶ms.values[0]))
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	GPU_batch_uniform_1i(self->batch, params.id, params.values[0]);
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *bpygpu_VertBatch_uniform_f32(BPyGPUBatch *self, PyObject *args)
 | 
						|
{
 | 
						|
	struct {
 | 
						|
		const char *id;
 | 
						|
		float values[4];
 | 
						|
	} params;
 | 
						|
 | 
						|
	if (!PyArg_ParseTuple(
 | 
						|
	        args, "sf|fff:uniform_f32",
 | 
						|
	        ¶ms.id,
 | 
						|
	        ¶ms.values[0], ¶ms.values[1], ¶ms.values[2], ¶ms.values[3]))
 | 
						|
	{
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	switch (PyTuple_GET_SIZE(args)) {
 | 
						|
		case 2: GPU_batch_uniform_1f(self->batch, params.id, params.values[0]); break;
 | 
						|
		case 3: GPU_batch_uniform_2f(self->batch, params.id, UNPACK2(params.values)); break;
 | 
						|
		case 4: GPU_batch_uniform_3f(self->batch, params.id, UNPACK3(params.values)); break;
 | 
						|
		case 5: GPU_batch_uniform_4f(self->batch, params.id, UNPACK4(params.values)); break;
 | 
						|
		default:
 | 
						|
			BLI_assert(0);
 | 
						|
	}
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
PyDoc_STRVAR(bpygpu_VertBatch_draw_doc,
 | 
						|
"TODO"
 | 
						|
);
 | 
						|
static PyObject *bpygpu_VertBatch_draw(BPyGPUBatch *self)
 | 
						|
{
 | 
						|
	if (!glIsProgram(self->batch->program)) {
 | 
						|
		PyErr_SetString(PyExc_ValueError,
 | 
						|
		                "batch program has not not set");
 | 
						|
	}
 | 
						|
	GPU_batch_draw(self->batch);
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *bpygpu_VertBatch_program_use_begin(BPyGPUBatch *self)
 | 
						|
{
 | 
						|
	if (!glIsProgram(self->batch->program)) {
 | 
						|
		PyErr_SetString(PyExc_ValueError,
 | 
						|
		                "batch program has not not set");
 | 
						|
	}
 | 
						|
	GPU_batch_program_use_begin(self->batch);
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *bpygpu_VertBatch_program_use_end(BPyGPUBatch *self)
 | 
						|
{
 | 
						|
	if (!glIsProgram(self->batch->program)) {
 | 
						|
		PyErr_SetString(PyExc_ValueError,
 | 
						|
		                "batch program has not not set");
 | 
						|
	}
 | 
						|
	GPU_batch_program_use_end(self->batch);
 | 
						|
	Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static struct PyMethodDef bpygpu_VertBatch_methods[] = {
 | 
						|
	{"vertbuf_add", (PyCFunction)bpygpu_VertBatch_vertbuf_add,
 | 
						|
	 METH_O, bpygpu_VertBatch_vertbuf_add_doc},
 | 
						|
	{"program_set", (PyCFunction)bpygpu_VertBatch_program_set,
 | 
						|
	 METH_O, bpygpu_VertBatch_program_set_doc},
 | 
						|
	{"program_set_builtin", (PyCFunction)bpygpu_VertBatch_program_set_builtin,
 | 
						|
	 METH_VARARGS | METH_KEYWORDS, bpygpu_VertBatch_program_set_builtin_doc},
 | 
						|
	{"uniform_bool", (PyCFunction)bpygpu_VertBatch_uniform_bool,
 | 
						|
	 METH_VARARGS, NULL},
 | 
						|
	{"uniform_i32", (PyCFunction)bpygpu_VertBatch_uniform_i32,
 | 
						|
	 METH_VARARGS, NULL},
 | 
						|
	{"uniform_f32", (PyCFunction)bpygpu_VertBatch_uniform_f32,
 | 
						|
	  METH_VARARGS, NULL},
 | 
						|
	{"draw", (PyCFunction) bpygpu_VertBatch_draw,
 | 
						|
	 METH_NOARGS, bpygpu_VertBatch_draw_doc},
 | 
						|
	{"program_use_begin", (PyCFunction)bpygpu_VertBatch_program_use_begin,
 | 
						|
	 METH_NOARGS, ""},
 | 
						|
	{"program_use_end", (PyCFunction)bpygpu_VertBatch_program_use_end,
 | 
						|
	 METH_NOARGS, ""},
 | 
						|
	{NULL, NULL, 0, NULL}
 | 
						|
};
 | 
						|
 | 
						|
#ifdef USE_GPU_PY_REFERENCES
 | 
						|
 | 
						|
static int bpygpu_Batch_traverse(BPyGPUBatch *self, visitproc visit, void *arg)
 | 
						|
{
 | 
						|
	Py_VISIT(self->references);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int bpygpu_Batch_clear(BPyGPUBatch *self)
 | 
						|
{
 | 
						|
	Py_CLEAR(self->references);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
static void bpygpu_Batch_dealloc(BPyGPUBatch *self)
 | 
						|
{
 | 
						|
	GPU_batch_discard(self->batch);
 | 
						|
 | 
						|
#ifdef USE_GPU_PY_REFERENCES
 | 
						|
	if (self->references) {
 | 
						|
		PyObject_GC_UnTrack(self);
 | 
						|
		bpygpu_Batch_clear(self);
 | 
						|
		Py_XDECREF(self->references);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	Py_TYPE(self)->tp_free(self);
 | 
						|
}
 | 
						|
 | 
						|
PyDoc_STRVAR(py_gpu_batch_doc,
 | 
						|
"GPUBatch(type, buf)\n"
 | 
						|
"\n"
 | 
						|
"Contains VAOs + VBOs + Shader representing a drawable entity."
 | 
						|
"\n"
 | 
						|
"   :param type: One of these primitive types: {\n"
 | 
						|
"       \"POINTS\",\n"
 | 
						|
"       \"LINES\",\n"
 | 
						|
"       \"TRIS\",\n"
 | 
						|
"       \"LINE_STRIP\",\n"
 | 
						|
"       \"LINE_LOOP\",\n"
 | 
						|
"       \"TRI_STRIP\",\n"
 | 
						|
"       \"TRI_FAN\",\n"
 | 
						|
"       \"LINES_ADJ\",\n"
 | 
						|
"       \"TRIS_ADJ\",\n"
 | 
						|
"       \"LINE_STRIP_ADJ\",\n"
 | 
						|
"       \"NONE\"}\n"
 | 
						|
"   :type type: `str`\n"
 | 
						|
"   :param buf: Vertex buffer.\n"
 | 
						|
"   :type buf: :class: `gpu.types.GPUVertBuf`\n"
 | 
						|
);
 | 
						|
PyTypeObject BPyGPUBatch_Type = {
 | 
						|
	PyVarObject_HEAD_INIT(NULL, 0)
 | 
						|
	.tp_name = "GPUBatch",
 | 
						|
	.tp_basicsize = sizeof(BPyGPUBatch),
 | 
						|
	.tp_dealloc = (destructor)bpygpu_Batch_dealloc,
 | 
						|
#ifdef USE_GPU_PY_REFERENCES
 | 
						|
	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
 | 
						|
	.tp_doc = py_gpu_batch_doc,
 | 
						|
	.tp_traverse = (traverseproc)bpygpu_Batch_traverse,
 | 
						|
	.tp_clear = (inquiry)bpygpu_Batch_clear,
 | 
						|
#else
 | 
						|
	.tp_flags = Py_TPFLAGS_DEFAULT,
 | 
						|
#endif
 | 
						|
	.tp_methods = bpygpu_VertBatch_methods,
 | 
						|
	.tp_new = bpygpu_Batch_new,
 | 
						|
};
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
 | 
						|
/* -------------------------------------------------------------------- */
 | 
						|
 | 
						|
/** \name Public API
 | 
						|
* \{ */
 | 
						|
 | 
						|
PyObject *BPyGPUBatch_CreatePyObject(GPUBatch *batch)
 | 
						|
{
 | 
						|
	BPyGPUBatch *self;
 | 
						|
 | 
						|
#ifdef USE_GPU_PY_REFERENCES
 | 
						|
	self = (BPyGPUBatch *)_PyObject_GC_New(&BPyGPUBatch_Type);
 | 
						|
	self->references = NULL;
 | 
						|
#else
 | 
						|
	self = PyObject_New(BPyGPUBatch, &BPyGPUBatch_Type);
 | 
						|
#endif
 | 
						|
 | 
						|
	self->batch = batch;
 | 
						|
 | 
						|
	return (PyObject *)self;
 | 
						|
}
 | 
						|
 | 
						|
/** \} */
 | 
						|
 | 
						|
#undef BPY_GPU_BATCH_CHECK_OBJ
 |