Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/python_api/examples/gpu.offscreen.1.py192
-rw-r--r--doc/python_api/sphinx_doc_gen.py6
-rw-r--r--source/blender/gpu/GPU_extensions.h1
-rw-r--r--source/blender/gpu/intern/gpu_extensions.c5
-rw-r--r--source/blender/python/intern/CMakeLists.txt1
-rw-r--r--source/blender/python/intern/gpu.c19
-rw-r--r--source/blender/python/intern/gpu.h2
-rw-r--r--source/blender/python/intern/gpu_offscreen.c424
8 files changed, 646 insertions, 4 deletions
diff --git a/doc/python_api/examples/gpu.offscreen.1.py b/doc/python_api/examples/gpu.offscreen.1.py
new file mode 100644
index 00000000000..06f0d915361
--- /dev/null
+++ b/doc/python_api/examples/gpu.offscreen.1.py
@@ -0,0 +1,192 @@
+# Draws an off-screen buffer and display it in the corner of the view.
+import bpy
+from bgl import *
+
+
+class OffScreenDraw(bpy.types.Operator):
+ bl_idname = "view3d.offscreen_draw"
+ bl_label = "View3D Offscreen Draw"
+
+ _handle_calc = None
+ _handle_draw = None
+ is_enabled = False
+
+ # manage draw handler
+ @staticmethod
+ def draw_callback_px(self, context):
+ scene = context.scene
+ aspect_ratio = scene.render.resolution_x / scene.render.resolution_y
+
+ self._update_offscreen(context, self._offscreen)
+ self._opengl_draw(context, self._texture, aspect_ratio, 0.2)
+
+ @staticmethod
+ def handle_add(self, context):
+ OffScreenDraw._handle_draw = bpy.types.SpaceView3D.draw_handler_add(
+ self.draw_callback_px, (self, context),
+ 'WINDOW', 'POST_PIXEL',
+ )
+
+ @staticmethod
+ def handle_remove():
+ if OffScreenDraw._handle_draw is not None:
+ bpy.types.SpaceView3D.draw_handler_remove(OffScreenDraw._handle_draw, 'WINDOW')
+
+ OffScreenDraw._handle_draw = None
+
+ # off-screen buffer
+ @staticmethod
+ def _setup_offscreen(context):
+ import gpu
+ scene = context.scene
+ aspect_ratio = scene.render.resolution_x / scene.render.resolution_y
+
+ try:
+ offscreen = gpu.offscreen.new(512, int(512 / aspect_ratio))
+ except Exception as e:
+ print(e)
+ offscreen = None
+
+ return offscreen
+
+ @staticmethod
+ def _update_offscreen(context, offscreen):
+ scene = context.scene
+ render = scene.render
+ camera = scene.camera
+
+ modelview_matrix = camera.matrix_world.inverted()
+ projection_matrix = camera.calc_matrix_camera(
+ render.resolution_x,
+ render.resolution_y,
+ render.pixel_aspect_x,
+ render.pixel_aspect_y,
+ )
+
+
+ offscreen.draw_view3d(
+ scene,
+ context.space_data,
+ context.region,
+ projection_matrix,
+ modelview_matrix,
+ )
+
+ @staticmethod
+ def _opengl_draw(context, texture, aspect_ratio, scale):
+ """
+ OpenGL code to draw a rectangle in the viewport
+ """
+
+ glDisable(GL_DEPTH_TEST)
+
+ # view setup
+ glMatrixMode(GL_PROJECTION)
+ glPushMatrix()
+ glLoadIdentity()
+
+ glMatrixMode(GL_MODELVIEW)
+ glPushMatrix()
+ glLoadIdentity()
+
+ glOrtho(-1, 1, -1, 1, -15, 15)
+ gluLookAt(0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
+
+ act_tex = Buffer(GL_INT, 1)
+ glGetIntegerv(GL_TEXTURE_2D, act_tex)
+
+ viewport = Buffer(GL_INT, 4)
+ glGetIntegerv(GL_VIEWPORT, viewport)
+
+ width = int(scale * viewport[2])
+ height = int(width / aspect_ratio)
+
+ glViewport(viewport[0], viewport[1], width, height)
+ glScissor(viewport[0], viewport[1], width, height)
+
+ # draw routine
+ glEnable(GL_TEXTURE_2D)
+ glActiveTexture(GL_TEXTURE0)
+
+ glBindTexture(GL_TEXTURE_2D, texture)
+
+ texco = [(1, 1), (0, 1), (0, 0), (1, 0)]
+ verco = [(1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0), (1.0, -1.0)]
+
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
+
+ glColor4f(1.0, 1.0, 1.0, 1.0)
+
+ glBegin(GL_QUADS)
+ for i in range(4):
+ glTexCoord3f(texco[i][0], texco[i][1], 0.0)
+ glVertex2f(verco[i][0], verco[i][1])
+ glEnd()
+
+ # restoring settings
+ glBindTexture(GL_TEXTURE_2D, act_tex[0])
+
+ glDisable(GL_TEXTURE_2D)
+
+ # reset view
+ glMatrixMode(GL_PROJECTION)
+ glPopMatrix()
+
+ glMatrixMode(GL_MODELVIEW)
+ glPopMatrix()
+
+ glViewport(viewport[0], viewport[1], viewport[2], viewport[3])
+ glScissor(viewport[0], viewport[1], viewport[2], viewport[3])
+
+ # operator functions
+ @classmethod
+ def poll(cls, context):
+ return context.area.type == 'VIEW_3D'
+
+ def modal(self, context, event):
+ if context.area:
+ context.area.tag_redraw()
+
+ return {'PASS_THROUGH'}
+
+ def invoke(self, context, event):
+ if OffScreenDraw.is_enabled:
+ self.cancel(context)
+
+ return {'FINISHED'}
+
+ else:
+ self._offscreen = OffScreenDraw._setup_offscreen(context)
+ if self._offscreen:
+ self._texture = self._offscreen.color_texture
+ else:
+ self.report({'ERROR'}, "Error initializing offscreen buffer. More details in the console")
+ return {'CANCELLED'}
+
+ OffScreenDraw.handle_add(self, context)
+ OffScreenDraw.is_enabled = True
+
+ if context.area:
+ context.area.tag_redraw()
+
+ context.window_manager.modal_handler_add(self)
+ return {'RUNNING_MODAL'}
+
+ def cancel(self, context):
+ OffScreenDraw.handle_remove()
+ OffScreenDraw.is_enabled = False
+
+ if context.area:
+ context.area.tag_redraw()
+
+
+def register():
+ bpy.utils.register_class(OffScreenDraw)
+
+
+def unregister():
+ bpy.utils.unregister_class(OffScreenDraw)
+
+
+if __name__ == "__main__":
+ register()
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index f68d6bf617a..1788b4cdcbc 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -261,6 +261,7 @@ else:
"bpy.utils.previews",
"bpy_extras",
"gpu",
+ "gpu.offscreen",
"mathutils",
"mathutils.geometry",
"mathutils.bvhtree",
@@ -1659,7 +1660,9 @@ def write_rst_contents(basepath):
# mathutils
"mathutils", "mathutils.geometry", "mathutils.bvhtree", "mathutils.kdtree", "mathutils.noise",
# misc
- "freestyle", "bgl", "blf", "gpu", "aud", "bpy_extras",
+ "freestyle", "bgl", "blf",
+ "gpu", "gpu.offscreen",
+ "aud", "bpy_extras",
# bmesh, submodules are in own page
"bmesh",
)
@@ -1799,6 +1802,7 @@ def write_rst_importable_modules(basepath):
# C_modules
"aud" : "Audio System",
"blf" : "Font Drawing",
+ "gpu.offscreen" : "GPU Off-Screen Buffer",
"bmesh" : "BMesh Module",
"bmesh.types" : "BMesh Types",
"bmesh.utils" : "BMesh Utilities",
diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h
index 2dadd38f432..685ae59c47b 100644
--- a/source/blender/gpu/GPU_extensions.h
+++ b/source/blender/gpu/GPU_extensions.h
@@ -188,6 +188,7 @@ void GPU_offscreen_unbind(GPUOffScreen *ofs, bool restore);
void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels);
int GPU_offscreen_width(const GPUOffScreen *ofs);
int GPU_offscreen_height(const GPUOffScreen *ofs);
+int GPU_offscreen_color_texture(const GPUOffScreen *ofs);
/* Builtin/Non-generated shaders */
typedef enum GPUProgramType {
diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.c
index ce82a67dcaf..02e57e4b505 100644
--- a/source/blender/gpu/intern/gpu_extensions.c
+++ b/source/blender/gpu/intern/gpu_extensions.c
@@ -1613,6 +1613,11 @@ int GPU_offscreen_height(const GPUOffScreen *ofs)
return ofs->color->h_orig;
}
+int GPU_offscreen_color_texture(const GPUOffScreen *ofs)
+{
+ return ofs->color->bindcode;
+}
+
/* GPUShader */
struct GPUShader {
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index cd2a9fd8584..f04bca75a8c 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -46,6 +46,7 @@ set(INC_SYS
set(SRC
gpu.c
+ gpu_offscreen.c
bpy.c
bpy_app.c
bpy_app_build_options.c
diff --git a/source/blender/python/intern/gpu.c b/source/blender/python/intern/gpu.c
index 65b2e97f461..aada3f6fc80 100644
--- a/source/blender/python/intern/gpu.c
+++ b/source/blender/python/intern/gpu.c
@@ -55,7 +55,7 @@
#define PY_MODULE_ADD_CONSTANT(module, name) PyModule_AddIntConstant(module, # name, name)
PyDoc_STRVAR(M_gpu_doc,
-"This module provides access to the GLSL shader."
+"This module provides access to the GLSL shader and Offscreen rendering functionalities."
);
static struct PyModuleDef gpumodule = {
PyModuleDef_HEAD_INIT,
@@ -309,12 +309,25 @@ static PyMethodDef meth_export_shader[] = {
{"export_shader", (PyCFunction)GPU_export_shader, METH_VARARGS | METH_KEYWORDS, GPU_export_shader_doc}
};
+/* -------------------------------------------------------------------- */
+/* Initialize Module */
+
PyObject *GPU_initPython(void)
{
- PyObject *module = PyInit_gpu();
+ PyObject *module;
+ PyObject *submodule;
+ PyObject *sys_modules = PyThreadState_GET()->interp->modules;
+
+ module = PyInit_gpu();
+
PyModule_AddObject(module, "export_shader", (PyObject *)PyCFunction_New(meth_export_shader, NULL));
- PyDict_SetItemString(PyImport_GetModuleDict(), "gpu", module);
+ /* gpu.offscreen */
+ PyModule_AddObject(module, "offscreen", (submodule = BPyInit_gpu_offscreen()));
+ PyDict_SetItemString(sys_modules, PyModule_GetName(submodule), submodule);
+ Py_INCREF(submodule);
+
+ PyDict_SetItemString(PyImport_GetModuleDict(), "gpu", module);
return module;
}
diff --git a/source/blender/python/intern/gpu.h b/source/blender/python/intern/gpu.h
index 82338869b9d..0da44a4eb87 100644
--- a/source/blender/python/intern/gpu.h
+++ b/source/blender/python/intern/gpu.h
@@ -36,4 +36,6 @@
PyObject *GPU_initPython(void);
+PyObject *BPyInit_gpu_offscreen(void);
+
#endif /* __GPU_H__ */
diff --git a/source/blender/python/intern/gpu_offscreen.c b/source/blender/python/intern/gpu_offscreen.c
new file mode 100644
index 00000000000..f8285c6139f
--- /dev/null
+++ b/source/blender/python/intern/gpu_offscreen.c
@@ -0,0 +1,424 @@
+/*
+ * ***** 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/intern/gpu_offscreen.c
+ * \ingroup pythonintern
+ *
+ * This file defines the offscreen functionalities of the 'gpu' module
+ * used for off-screen OpenGL rendering.
+ */
+
+#include <Python.h>
+
+#include "BLI_utildefines.h"
+
+#include "WM_types.h"
+
+#include "ED_screen.h"
+
+#include "GPU_extensions.h"
+#include "GPU_compositing.h"
+
+#include "../mathutils/mathutils.h"
+
+#include "../generic/py_capi_utils.h"
+
+#include "gpu.h"
+
+#include "ED_view3d.h"
+
+/* -------------------------------------------------------------------- */
+/* GPU Offscreen PyObject */
+
+typedef struct {
+ PyObject_HEAD
+ GPUOffScreen *ofs;
+} BPy_GPUOffScreen;
+
+static int bpy_gpu_offscreen_valid_check(BPy_GPUOffScreen *py_gpu_ofs)
+{
+ if (UNLIKELY(py_gpu_ofs->ofs == NULL)) {
+ PyErr_SetString(PyExc_ReferenceError, "GPU offscreen was freed, no further access is valid");
+ return -1;
+ }
+ return 0;
+}
+
+#define BPY_GPU_OFFSCREEN_CHECK_OBJ(pygpu) { \
+ if (UNLIKELY(bpy_gpu_offscreen_valid_check(pygpu) == -1)) { \
+ return NULL; \
+ } \
+} ((void)0)
+
+PyDoc_STRVAR(pygpu_offscreen_width_doc, "Texture width.\n\n:type: int");
+static PyObject *pygpu_offscreen_width_get(BPy_GPUOffScreen *self, void *UNUSED(type))
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+ return PyLong_FromLong(GPU_offscreen_width(self->ofs));
+}
+
+PyDoc_STRVAR(pygpu_offscreen_height_doc, "Texture height.\n\n:type: int");
+static PyObject *pygpu_offscreen_height_get(BPy_GPUOffScreen *self, void *UNUSED(type))
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+ return PyLong_FromLong(GPU_offscreen_height(self->ofs));
+}
+
+PyDoc_STRVAR(pygpu_offscreen_color_texture_doc, "Color texture.\n\n:type: int");
+static PyObject *pygpu_offscreen_color_texture_get(BPy_GPUOffScreen *self, void *UNUSED(type))
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+ return PyLong_FromLong(GPU_offscreen_color_texture(self->ofs));
+}
+
+PyDoc_STRVAR(pygpu_offscreen_bind_doc,
+"bind(save=True)\n"
+"\n"
+" Bind the offscreen object.\n"
+"\n"
+" :param save: save OpenGL current states.\n"
+" :type save: bool\n"
+);
+static PyObject *pygpu_offscreen_bind(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
+{
+ static const char *kwlist[] = {"save", NULL};
+ bool save = true;
+
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "|O&:bind", (char **)(kwlist),
+ PyC_ParseBool, &save))
+ {
+ return NULL;
+ }
+
+ GPU_offscreen_bind(self->ofs, save);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_offscreen_unbind_doc,
+"unbind(restore=True)\n"
+"\n"
+" Unbind the offscreen object.\n"
+"\n"
+" :param restore: restore OpenGL previous states.\n"
+" :type restore: bool\n"
+);
+static PyObject *pygpu_offscreen_unbind(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
+{
+ static const char *kwlist[] = {"restore", NULL};
+ bool restore = true;
+
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "|O&:unbind", (char **)(kwlist),
+ PyC_ParseBool, &restore))
+ {
+ return NULL;
+ }
+
+ GPU_offscreen_unbind(self->ofs, restore);
+ Py_RETURN_NONE;
+}
+
+/**
+ * Use with PyArg_ParseTuple's "O&" formatting.
+ */
+static int pygpu_offscreen_check_matrix(PyObject *o, void *p)
+{
+ MatrixObject **pymat_p = p;
+ MatrixObject *pymat = (MatrixObject *)o;
+
+ if (!MatrixObject_Check(pymat)) {
+ PyErr_Format(PyExc_TypeError,
+ "expected a mathutils.Matrix, not a %.200s",
+ Py_TYPE(o)->tp_name);
+ return 0;
+ }
+
+ if (BaseMath_ReadCallback(pymat) == -1) {
+ return 0;
+ }
+
+ if ((pymat->num_col != 4) ||
+ (pymat->num_row != 4))
+ {
+ PyErr_SetString(PyExc_ValueError, "matrix must be 4x4");
+ return 0;
+ }
+
+ *pymat_p = pymat;
+ return 1;
+}
+
+PyDoc_STRVAR(pygpu_offscreen_draw_view3d_doc,
+"draw_view3d(scene, view3d, region, modelview_matrix, projection_matrix)\n"
+"\n"
+" Draw the 3d viewport in the offscreen object.\n"
+"\n"
+" :param scene: Scene to draw.\n"
+" :type scene: :class:`bpy.types.Scene`\n"
+" :param view3d: 3D View to get the drawing settings from.\n"
+" :type view3d: :class:`bpy.types.SpaceView3D`\n"
+" :param region: Region of the 3D View.\n"
+" :type region: :class:`bpy.types.Region`\n"
+" :param modelview_matrix: ModelView Matrix.\n"
+" :type modelview_matrix: :class:`mathutils.Matrix`\n"
+" :param projection_matrix: Projection Matrix.\n"
+" :type projection_matrix: :class:`mathutils.Matrix`\n"
+);
+static PyObject *pygpu_offscreen_draw_view3d(BPy_GPUOffScreen *self, PyObject *args, PyObject *kwds)
+{
+ static const char *kwlist[] = {"scene", "view3d", "region", "projection_matrix", "modelview_matrix", NULL};
+
+ MatrixObject *py_mat_modelview, *py_mat_projection;
+ PyObject *py_scene, *py_region, *py_view3d;
+
+ Scene *scene;
+ View3D *v3d;
+ ARegion *ar;
+ GPUFX *fx;
+ GPUFXSettings fx_settings;
+
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "OOOO&O&:draw_view3d", (char **)(kwlist),
+ &py_scene, &py_view3d, &py_region,
+ pygpu_offscreen_check_matrix, &py_mat_projection,
+ pygpu_offscreen_check_matrix, &py_mat_modelview) ||
+ (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) ||
+ !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
+ !(ar = PyC_RNA_AsPointer(py_region, "Region"))))
+ {
+ return NULL;
+ }
+
+ fx = GPU_fx_compositor_create();
+
+ fx_settings = v3d->fx_settings; /* full copy */
+
+ ED_view3d_draw_offscreen_init(scene, v3d);
+
+ GPU_offscreen_bind(self->ofs, true); /* bind */
+
+ ED_view3d_draw_offscreen(
+ scene, v3d, ar, GPU_offscreen_width(self->ofs), GPU_offscreen_height(self->ofs),
+ (float(*)[4])py_mat_modelview->matrix, (float(*)[4])py_mat_projection->matrix,
+ false, true, true, "",
+ fx, &fx_settings,
+ self->ofs);
+
+ GPU_fx_compositor_destroy(fx);
+ GPU_offscreen_unbind(self->ofs, true); /* unbind */
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_offscreen_free_doc,
+"free()\n"
+"\n"
+" Free the offscreen object\n"
+" The framebuffer, texture and render objects will no longer be accessible.\n"
+);
+static PyObject *pygpu_offscreen_free(BPy_GPUOffScreen *self)
+{
+ BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
+
+ GPU_offscreen_free(self->ofs);
+ self->ofs = NULL;
+ Py_RETURN_NONE;
+}
+
+static void BPy_GPUOffScreen__tp_dealloc(BPy_GPUOffScreen *self)
+{
+ if (self->ofs)
+ GPU_offscreen_free(self->ofs);
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+static PyGetSetDef bpy_gpu_offscreen_getseters[] = {
+ {(char *)"color_texture", (getter)pygpu_offscreen_color_texture_get, (setter)NULL, pygpu_offscreen_color_texture_doc, NULL},
+ {(char *)"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
+ {(char *)"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
+ {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
+};
+
+static struct PyMethodDef bpy_gpu_offscreen_methods[] = {
+ {"bind", (PyCFunction)pygpu_offscreen_bind, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_bind_doc},
+ {"unbind", (PyCFunction)pygpu_offscreen_unbind, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_unbind_doc},
+ {"draw_view3d", (PyCFunction)pygpu_offscreen_draw_view3d, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_draw_view3d_doc},
+ {"free", (PyCFunction)pygpu_offscreen_free, METH_NOARGS, pygpu_offscreen_free_doc},
+ {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(py_gpu_offscreen_doc,
+".. class:: GPUOffscreen"
+"\n"
+" This object gives access to off screen buffers.\n"
+);
+static PyTypeObject BPy_GPUOffScreen_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "GPUOffScreen", /* tp_name */
+ sizeof(BPy_GPUOffScreen), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)BPy_GPUOffScreen__tp_dealloc, /* tp_dealloc */
+ NULL, /* tp_print */
+ NULL, /* tp_getattr */
+ NULL, /* tp_setattr */
+ NULL, /* tp_compare */
+ NULL, /* tp_repr */
+ NULL, /* tp_as_number */
+ NULL, /* tp_as_sequence */
+ NULL, /* tp_as_mapping */
+ NULL, /* tp_hash */
+ NULL, /* tp_call */
+ NULL, /* tp_str */
+ NULL, /* tp_getattro */
+ NULL, /* tp_setattro */
+ NULL, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
+ py_gpu_offscreen_doc, /* Documentation string */
+ NULL, /* tp_traverse */
+ NULL, /* tp_clear */
+ NULL, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ NULL, /* tp_iter */
+ NULL, /* tp_iternext */
+ bpy_gpu_offscreen_methods, /* tp_methods */
+ NULL, /* tp_members */
+ bpy_gpu_offscreen_getseters, /* tp_getset */
+ NULL, /* tp_base */
+ NULL, /* tp_dict */
+ NULL, /* tp_descr_get */
+ NULL, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ NULL, /* tp_alloc */
+ NULL, /* tp_new */
+ (freefunc)0, /* tp_free */
+ NULL, /* tp_is_gc */
+ NULL, /* tp_bases */
+ NULL, /* tp_mro */
+ NULL, /* tp_cache */
+ NULL, /* tp_subclasses */
+ NULL, /* tp_weaklist */
+ (destructor) NULL /* tp_del */
+};
+
+/* -------------------------------------------------------------------- */
+/* GPU offscreen methods */
+
+static PyObject *BPy_GPU_OffScreen_CreatePyObject(GPUOffScreen *ofs)
+{
+ BPy_GPUOffScreen *self;
+ self = PyObject_New(BPy_GPUOffScreen, &BPy_GPUOffScreen_Type);
+ self->ofs = ofs;
+ return (PyObject *)self;
+}
+
+PyDoc_STRVAR(pygpu_offscreen_new_doc,
+"new(width, height, samples=0)\n"
+"\n"
+" Return a GPUOffScreen.\n"
+"\n"
+" :param width: Horizontal dimension of the buffer.\n"
+" :type width: int`\n"
+" :param height: Vertical dimension of the buffer.\n"
+" :type height: int`\n"
+" :param samples: OpenGL samples to use for MSAA or zero to disable.\n"
+" :type samples: int\n"
+" :return: Newly created off-screen buffer.\n"
+" :rtype: :class:`gpu.GPUOffscreen`\n"
+);
+static PyObject *pygpu_offscreen_new(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
+{
+ static const char *kwlist[] = {"width", "height", "samples", NULL};
+
+ GPUOffScreen *ofs;
+ int width, height, samples = 0;
+ char err_out[256];
+
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwds, "ii|i:new", (char **)(kwlist),
+ &width, &height, &samples))
+ {
+ return NULL;
+ }
+
+ ofs = GPU_offscreen_create(width, height, samples, err_out);
+
+ if (ofs == NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "gpu.offscreen.new(...) failed with '%s'",
+ err_out[0] ? err_out : "unknown error");
+ return NULL;
+ }
+
+ return BPy_GPU_OffScreen_CreatePyObject(ofs);
+}
+
+static struct PyMethodDef BPy_GPU_offscreen_methods[] = {
+ {"new", (PyCFunction)pygpu_offscreen_new, METH_VARARGS | METH_KEYWORDS, pygpu_offscreen_new_doc},
+ {NULL, NULL, 0, NULL}
+};
+
+PyDoc_STRVAR(BPy_GPU_offscreen_doc,
+"This module provides access to offscreen rendering functions."
+);
+static PyModuleDef BPy_GPU_offscreen_module_def = {
+ PyModuleDef_HEAD_INIT,
+ "gpu.offscreen", /* m_name */
+ BPy_GPU_offscreen_doc, /* m_doc */
+ 0, /* m_size */
+ BPy_GPU_offscreen_methods, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL, /* m_free */
+};
+
+PyObject *BPyInit_gpu_offscreen(void)
+{
+ PyObject *submodule;
+
+ /* Register the 'GPUOffscreen' class */
+ if (PyType_Ready(&BPy_GPUOffScreen_Type)) {
+ return NULL;
+ }
+
+ submodule = PyModule_Create(&BPy_GPU_offscreen_module_def);
+
+#define MODULE_TYPE_ADD(s, t) \
+ PyModule_AddObject(s, t.tp_name, (PyObject *)&t); Py_INCREF((PyObject *)&t)
+
+ MODULE_TYPE_ADD(submodule, BPy_GPUOffScreen_Type);
+
+#undef MODULE_TYPE_ADD
+
+ return submodule;
+}
+
+#undef BPY_GPU_OFFSCREEN_CHECK_OBJ