Blender  V2.93
gpu_py_offscreen.c
Go to the documentation of this file.
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  * Copyright 2015, Blender Foundation.
17  */
18 
29 #include <Python.h>
30 
31 #include "MEM_guardedalloc.h"
32 
33 #include "BLI_string.h"
34 #include "BLI_utildefines.h"
35 
36 #include "BKE_global.h"
37 #include "BKE_lib_id.h"
38 #include "BKE_scene.h"
39 
40 #include "DNA_scene_types.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_view3d_types.h"
43 
44 #include "GPU_context.h"
45 #include "GPU_framebuffer.h"
46 #include "GPU_texture.h"
47 
48 #include "ED_view3d.h"
49 #include "ED_view3d_offscreen.h"
50 
51 #include "../mathutils/mathutils.h"
52 
53 #include "../generic/py_capi_utils.h"
54 
55 #include "gpu_py.h"
56 #include "gpu_py_offscreen.h" /* own include */
57 
58 /* Define the free method to avoid breakage. */
59 #define BPYGPU_USE_GPUOBJ_FREE_METHOD
60 
61 /* -------------------------------------------------------------------- */
66 {
67  if (UNLIKELY(py_ofs->ofs == NULL)) {
68  PyErr_SetString(PyExc_ReferenceError,
70  "GPU offscreen was freed, no further access is valid"
71 #else
72  "GPU offscreen: internal error"
73 #endif
74  );
75  return -1;
76  }
77  return 0;
78 }
79 
80 #define BPY_GPU_OFFSCREEN_CHECK_OBJ(bpygpu) \
81  { \
82  if (UNLIKELY(pygpu_offscreen_valid_check(bpygpu) == -1)) { \
83  return NULL; \
84  } \
85  } \
86  ((void)0)
87 
90 /* -------------------------------------------------------------------- */
97 typedef struct {
98  PyObject_HEAD /* required python macro */
100  int level;
101  bool is_explicitly_bound; /* Bound by "bind" method. */
103 
105 {
106  Py_DECREF(self->py_offscreen);
107  PyObject_DEL(self);
108 }
109 
111 {
112  BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offscreen);
113 
114  if (!self->is_explicitly_bound) {
115  if (self->level != -1) {
116  PyErr_SetString(PyExc_RuntimeError, "Already in use");
117  return NULL;
118  }
119 
120  GPU_offscreen_bind(self->py_offscreen->ofs, true);
121  self->level = GPU_framebuffer_stack_level_get();
122  }
123 
124  Py_RETURN_NONE;
125 }
126 
128  PyObject *UNUSED(args))
129 {
130  BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offscreen);
131 
132  if (self->level == -1) {
133  PyErr_SetString(PyExc_RuntimeError, "Not yet in use\n");
134  return NULL;
135  }
136 
137  const int level = GPU_framebuffer_stack_level_get();
138  if (level != self->level) {
139  PyErr_Format(
140  PyExc_RuntimeError, "Level of bind mismatch, expected %d, got %d\n", self->level, level);
141  }
142 
143  GPU_offscreen_unbind(self->py_offscreen->ofs, true);
144  Py_RETURN_NONE;
145 }
146 
148  {"__enter__", (PyCFunction)pygpu_offscreen_stack_context_enter, METH_NOARGS},
149  {"__exit__", (PyCFunction)pygpu_offscreen_stack_context_exit, METH_VARARGS},
150  {NULL},
151 };
152 
153 static PyTypeObject PyGPUOffscreenStackContext_Type = {
154  PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUFrameBufferStackContext",
155  .tp_basicsize = sizeof(OffScreenStackContext),
156  .tp_dealloc = (destructor)pygpu_offscreen_stack_context__tp_dealloc,
157  .tp_flags = Py_TPFLAGS_DEFAULT,
159 };
160 
161 PyDoc_STRVAR(pygpu_offscreen_bind_doc,
162  ".. function:: bind()\n"
163  "\n"
164  " Context manager to ensure balanced bind calls, even in the case of an error.\n");
165 static PyObject *pygpu_offscreen_bind(BPyGPUOffScreen *self)
166 {
169  ret->py_offscreen = self;
170  ret->level = -1;
171  ret->is_explicitly_bound = false;
172  Py_INCREF(self);
173 
175  ret->is_explicitly_bound = true;
176 
177  return (PyObject *)ret;
178 }
179 
180 PyDoc_STRVAR(pygpu_offscreen_unbind_doc,
181  ".. method:: unbind(restore=True)\n"
182  "\n"
183  " Unbind the offscreen object.\n"
184  "\n"
185  " :arg restore: Restore the OpenGL state, can only be used when the state has been "
186  "saved before.\n"
187  " :type restore: bool\n");
188 static PyObject *pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
189 {
190  bool restore = true;
191 
193 
194  static const char *_keywords[] = {"restore", NULL};
195  static _PyArg_Parser _parser = {"|O&:unbind", _keywords, 0};
196  if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &restore)) {
197  return NULL;
198  }
199 
200  GPU_offscreen_unbind(self->ofs, restore);
201  GPU_apply_state();
202  Py_RETURN_NONE;
203 }
204 
207 /* -------------------------------------------------------------------- */
211 static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self),
212  PyObject *args,
213  PyObject *kwds)
214 {
216 
217  GPUOffScreen *ofs = NULL;
218  int width, height;
219  char err_out[256];
220 
221  static const char *_keywords[] = {"width", "height", NULL};
222  static _PyArg_Parser _parser = {"ii:GPUOffScreen.__new__", _keywords, 0};
223  if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &width, &height)) {
224  return NULL;
225  }
226 
227  if (GPU_context_active_get()) {
228  ofs = GPU_offscreen_create(width, height, true, false, err_out);
229  }
230  else {
231  STRNCPY(err_out, "No active GPU context found");
232  }
233 
234  if (ofs == NULL) {
235  PyErr_Format(PyExc_RuntimeError,
236  "gpu.offscreen.new(...) failed with '%s'",
237  err_out[0] ? err_out : "unknown error");
238  return NULL;
239  }
240 
241  return BPyGPUOffScreen_CreatePyObject(ofs);
242 }
243 
244 PyDoc_STRVAR(pygpu_offscreen_width_doc, "Width of the texture.\n\n:type: `int`");
245 static PyObject *pygpu_offscreen_width_get(BPyGPUOffScreen *self, void *UNUSED(type))
246 {
248  return PyLong_FromLong(GPU_offscreen_width(self->ofs));
249 }
250 
251 PyDoc_STRVAR(pygpu_offscreen_height_doc, "Height of the texture.\n\n:type: `int`");
252 static PyObject *pygpu_offscreen_height_get(BPyGPUOffScreen *self, void *UNUSED(type))
253 {
255  return PyLong_FromLong(GPU_offscreen_height(self->ofs));
256 }
257 
258 PyDoc_STRVAR(pygpu_offscreen_color_texture_doc,
259  "OpenGL bindcode for the color texture.\n\n:type: `int`");
261 {
264  return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
265 }
266 
268  pygpu_offscreen_draw_view3d_doc,
269  ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, do_color_management=False)\n"
270  "\n"
271  " Draw the 3d viewport in the offscreen object.\n"
272  "\n"
273  " :arg scene: Scene to draw.\n"
274  " :type scene: :class:`bpy.types.Scene`\n"
275  " :arg view_layer: View layer to draw.\n"
276  " :type view_layer: :class:`bpy.types.ViewLayer`\n"
277  " :arg view3d: 3D View to get the drawing settings from.\n"
278  " :type view3d: :class:`bpy.types.SpaceView3D`\n"
279  " :arg region: Region of the 3D View (required as temporary draw target).\n"
280  " :type region: :class:`bpy.types.Region`\n"
281  " :arg view_matrix: View Matrix (e.g. ``camera.matrix_world.inverted()``).\n"
282  " :type view_matrix: :class:`mathutils.Matrix`\n"
283  " :arg projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n"
284  " :type projection_matrix: :class:`mathutils.Matrix`\n"
285  " :arg do_color_management: Color manage the output.\n"
286  " :type do_color_management: bool\n");
287 static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
288 {
289  MatrixObject *py_mat_view, *py_mat_projection;
290  PyObject *py_scene, *py_view_layer, *py_region, *py_view3d;
291 
292  struct Depsgraph *depsgraph;
293  struct Scene *scene;
294  struct ViewLayer *view_layer;
295  View3D *v3d;
296  ARegion *region;
297 
298  bool do_color_management = false;
299 
301 
302  static const char *_keywords[] = {
303  "scene", "view_layer", "view3d", "region", "view_matrix", "projection_matrix", "do_color_management", NULL};
304 
305  static _PyArg_Parser _parser = {"OOOOO&O&|$O&:draw_view3d", _keywords, 0};
306  if (!_PyArg_ParseTupleAndKeywordsFast(args,
307  kwds,
308  &_parser,
309  &py_scene,
310  &py_view_layer,
311  &py_view3d,
312  &py_region,
314  &py_mat_view,
316  &py_mat_projection,
317  PyC_ParseBool,
318  &do_color_management) ||
319  (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) ||
320  !(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) ||
321  !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
322  !(region = PyC_RNA_AsPointer(py_region, "Region")))) {
323  return NULL;
324  }
325 
327 
329 
330  /* Disable 'bgl' state since it interfere with off-screen drawing, see: T84402. */
331  const bool is_bgl = GPU_bgl_get();
332  if (is_bgl) {
333  GPU_bgl_end();
334  }
335 
336  GPU_offscreen_bind(self->ofs, true);
337 
339  scene,
340  v3d->shading.type,
341  v3d,
342  region,
345  (const float(*)[4])py_mat_view->matrix,
346  (const float(*)[4])py_mat_projection->matrix,
347  true,
348  true,
349  "",
350  do_color_management,
351  true,
352  self->ofs,
353  NULL);
354 
355  GPU_offscreen_unbind(self->ofs, true);
356 
357  if (is_bgl) {
358  GPU_bgl_start();
359  }
360 
361  Py_RETURN_NONE;
362 }
363 
364 #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
365 PyDoc_STRVAR(pygpu_offscreen_free_doc,
366  ".. method:: free()\n"
367  "\n"
368  " Free the offscreen object.\n"
369  " The framebuffer, texture and render objects will no longer be accessible.\n");
370 static PyObject *pygpu_offscreen_free(BPyGPUOffScreen *self)
371 {
373 
374  GPU_offscreen_free(self->ofs);
375  self->ofs = NULL;
376  Py_RETURN_NONE;
377 }
378 #endif
379 
381 {
382  if (self->ofs) {
383  GPU_offscreen_free(self->ofs);
384  }
385  Py_TYPE(self)->tp_free((PyObject *)self);
386 }
387 
388 static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
389  {"color_texture",
391  (setter)NULL,
392  pygpu_offscreen_color_texture_doc,
393  NULL},
394  {"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
395  {"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
396  {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
397 };
398 
399 static struct PyMethodDef pygpu_offscreen__tp_methods[] = {
400  {"bind", (PyCFunction)pygpu_offscreen_bind, METH_NOARGS, pygpu_offscreen_bind_doc},
401  {"unbind",
402  (PyCFunction)pygpu_offscreen_unbind,
403  METH_VARARGS | METH_KEYWORDS,
404  pygpu_offscreen_unbind_doc},
405  {"draw_view3d",
406  (PyCFunction)pygpu_offscreen_draw_view3d,
407  METH_VARARGS | METH_KEYWORDS,
408  pygpu_offscreen_draw_view3d_doc},
409 #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
410  {"free", (PyCFunction)pygpu_offscreen_free, METH_NOARGS, pygpu_offscreen_free_doc},
411 #endif
412  {NULL, NULL, 0, NULL},
413 };
414 
415 PyDoc_STRVAR(pygpu_offscreen__tp_doc,
416  ".. class:: GPUOffScreen(width, height)\n"
417  "\n"
418  " This object gives access to off screen buffers.\n"
419  "\n"
420  " :arg width: Horizontal dimension of the buffer.\n"
421  " :type width: int\n"
422  " :arg height: Vertical dimension of the buffer.\n"
423  " :type height: int\n");
424 PyTypeObject BPyGPUOffScreen_Type = {
425  PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUOffScreen",
426  .tp_basicsize = sizeof(BPyGPUOffScreen),
427  .tp_dealloc = (destructor)BPyGPUOffScreen__tp_dealloc,
428  .tp_flags = Py_TPFLAGS_DEFAULT,
429  .tp_doc = pygpu_offscreen__tp_doc,
430  .tp_methods = pygpu_offscreen__tp_methods,
431  .tp_getset = pygpu_offscreen__tp_getseters,
432  .tp_new = pygpu_offscreen__tp_new,
433 };
434 
437 /* -------------------------------------------------------------------- */
442 {
443  BPyGPUOffScreen *self;
444 
445  self = PyObject_New(BPyGPUOffScreen, &BPyGPUOffScreen_Type);
446  self->ofs = ofs;
447 
448  return (PyObject *)self;
449 }
450 
453 #undef BPY_GPU_OFFSCREEN_CHECK_OBJ
#define G_MAIN
Definition: BKE_global.h:232
bool BKE_id_is_in_global_main(struct ID *id)
Definition: lib_id.c:2287
struct Depsgraph * BKE_scene_ensure_depsgraph(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer)
Definition: scene.c:3526
#define BLI_assert(a)
Definition: BLI_assert.h:58
#define STRNCPY(dst, src)
Definition: BLI_string.h:163
#define UNUSED(x)
#define UNLIKELY(x)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:51
void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph, const struct Scene *scene, eDrawType drawtype, struct View3D *v3d, struct ARegion *region, int winx, int winy, const float viewmat[4][4], const float winmat[4][4], bool is_image_render, bool draw_background, const char *viewname, const bool do_color_management, const bool restore_rv3d_mats, struct GPUOffScreen *ofs, struct GPUViewport *viewport)
GPUContext * GPU_context_active_get(void)
Definition: gpu_context.cc:136
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei width
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
void GPU_bgl_end(void)
Definition: gpu_state.cc:367
void GPU_bgl_start(void)
Definition: gpu_state.cc:336
void GPU_apply_state(void)
Definition: gpu_state.cc:321
bool GPU_bgl_get(void)
Definition: gpu_state.cc:381
struct GPUTexture GPUTexture
Definition: GPU_texture.h:33
int GPU_texture_opengl_bindcode(const GPUTexture *tex)
Definition: gpu_texture.cc:585
Read Guarded memory(de)allocation.
PyObject * self
Definition: bpy_driver.c:185
Scene scene
const Depsgraph * depsgraph
uint GPU_framebuffer_stack_level_get(void)
void GPU_offscreen_free(GPUOffScreen *ofs)
void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore)
GPUOffScreen * GPU_offscreen_create(int width, int height, bool depth, bool high_bitdepth, char err_out[256])
int GPU_offscreen_width(const GPUOffScreen *ofs)
void GPU_offscreen_bind(GPUOffScreen *ofs, bool save)
GPUTexture * GPU_offscreen_color_texture(const GPUOffScreen *ofs)
int GPU_offscreen_height(const GPUOffScreen *ofs)
#define BPYGPU_IS_INIT_OR_ERROR_OBJ
Definition: gpu_py.h:28
static PyMethodDef pygpu_offscreen_stack_context__tp_methods[]
PyDoc_STRVAR(pygpu_offscreen_bind_doc, ".. function:: bind()\n" "\n" " Context manager to ensure balanced bind calls, even in the case of an error.\n")
#define BPY_GPU_OFFSCREEN_CHECK_OBJ(bpygpu)
#define BPYGPU_USE_GPUOBJ_FREE_METHOD
static PyObject * pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self), PyObject *args, PyObject *kwds)
static int pygpu_offscreen_valid_check(BPyGPUOffScreen *py_ofs)
static PyObject * pygpu_offscreen_stack_context_exit(OffScreenStackContext *self, PyObject *UNUSED(args))
static struct PyMethodDef pygpu_offscreen__tp_methods[]
static PyObject * pygpu_offscreen_width_get(BPyGPUOffScreen *self, void *UNUSED(type))
static void BPyGPUOffScreen__tp_dealloc(BPyGPUOffScreen *self)
static PyObject * pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
PyTypeObject BPyGPUOffScreen_Type
static PyObject * pygpu_offscreen_bind(BPyGPUOffScreen *self)
static PyObject * pygpu_offscreen_stack_context_enter(OffScreenStackContext *self)
static PyObject * pygpu_offscreen_height_get(BPyGPUOffScreen *self, void *UNUSED(type))
static PyObject * pygpu_offscreen_free(BPyGPUOffScreen *self)
static PyTypeObject PyGPUOffscreenStackContext_Type
static void pygpu_offscreen_stack_context__tp_dealloc(OffScreenStackContext *self)
static PyObject * pygpu_offscreen_color_texture_get(BPyGPUOffScreen *self, void *UNUSED(type))
static PyGetSetDef pygpu_offscreen__tp_getseters[]
PyObject * BPyGPUOffScreen_CreatePyObject(GPUOffScreen *ofs)
static PyObject * pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
struct BPyGPUOffScreen BPyGPUOffScreen
int Matrix_Parse4x4(PyObject *o, void *p)
void * PyC_RNA_AsPointer(PyObject *value, const char *type_name)
int PyC_ParseBool(PyObject *o, void *p)
return ret
PyObject_HEAD struct GPUOffScreen * ofs
PyObject_HEAD BPyGPUOffScreen * py_offscreen
View3DShading shading