Blender  V3.3
gpu_py_offscreen.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2015 Blender Foundation. */
3 
14 #include <Python.h>
15 
16 #include "BLI_string.h"
17 #include "BLI_utildefines.h"
18 
19 #include "BKE_global.h"
20 #include "BKE_lib_id.h"
21 #include "BKE_scene.h"
22 
23 #include "DNA_scene_types.h"
24 #include "DNA_screen_types.h"
25 #include "DNA_view3d_types.h"
26 
27 #include "GPU_context.h"
28 #include "GPU_framebuffer.h"
29 #include "GPU_texture.h"
30 #include "GPU_viewport.h"
31 
32 #include "ED_view3d_offscreen.h"
33 
34 #include "../mathutils/mathutils.h"
35 
36 #include "../generic/py_capi_utils.h"
37 
38 #include "gpu_py.h"
39 #include "gpu_py_texture.h"
40 
41 #include "gpu_py_offscreen.h" /* own include */
42 
43 /* Define the free method to avoid breakage. */
44 #define BPYGPU_USE_GPUOBJ_FREE_METHOD
45 
46 /* -------------------------------------------------------------------- */
51  {GPU_RGBA8, "RGBA8"},
52  {GPU_RGBA16, "RGBA16"},
53  {GPU_RGBA16F, "RGBA16F"},
54  {GPU_RGBA32F, "RGBA32F"},
55  {0, NULL},
56 };
57 
59 {
60  if (UNLIKELY(py_ofs->ofs == NULL)) {
61  PyErr_SetString(PyExc_ReferenceError,
63  "GPU offscreen was freed, no further access is valid"
64 #else
65  "GPU offscreen: internal error"
66 #endif
67  );
68  return -1;
69  }
70  return 0;
71 }
72 
73 #define BPY_GPU_OFFSCREEN_CHECK_OBJ(bpygpu) \
74  { \
75  if (UNLIKELY(pygpu_offscreen_valid_check(bpygpu) == -1)) { \
76  return NULL; \
77  } \
78  } \
79  ((void)0)
80 
83 /* -------------------------------------------------------------------- */
90 typedef struct {
91  PyObject_HEAD /* Required Python macro. */
93  int level;
94  bool is_explicitly_bound; /* Bound by "bind" method. */
96 
98 {
99  Py_DECREF(self->py_offscreen);
100  PyObject_DEL(self);
101 }
102 
104 {
105  BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offscreen);
106 
107  if (!self->is_explicitly_bound) {
108  if (self->level != -1) {
109  PyErr_SetString(PyExc_RuntimeError, "Already in use");
110  return NULL;
111  }
112 
113  GPU_offscreen_bind(self->py_offscreen->ofs, true);
114  self->level = GPU_framebuffer_stack_level_get();
115  }
116 
117  Py_RETURN_NONE;
118 }
119 
121  PyObject *UNUSED(args))
122 {
123  BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offscreen);
124 
125  if (self->level == -1) {
126  PyErr_SetString(PyExc_RuntimeError, "Not yet in use\n");
127  return NULL;
128  }
129 
130  const int level = GPU_framebuffer_stack_level_get();
131  if (level != self->level) {
132  PyErr_Format(
133  PyExc_RuntimeError, "Level of bind mismatch, expected %d, got %d\n", self->level, level);
134  }
135 
136  GPU_offscreen_unbind(self->py_offscreen->ofs, true);
137  Py_RETURN_NONE;
138 }
139 
141  {"__enter__", (PyCFunction)pygpu_offscreen_stack_context_enter, METH_NOARGS},
142  {"__exit__", (PyCFunction)pygpu_offscreen_stack_context_exit, METH_VARARGS},
143  {NULL},
144 };
145 
146 static PyTypeObject PyGPUOffscreenStackContext_Type = {
147  PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUFrameBufferStackContext",
148  .tp_basicsize = sizeof(OffScreenStackContext),
149  .tp_dealloc = (destructor)pygpu_offscreen_stack_context__tp_dealloc,
150  .tp_flags = Py_TPFLAGS_DEFAULT,
152 };
153 
154 PyDoc_STRVAR(pygpu_offscreen_bind_doc,
155  ".. function:: bind()\n"
156  "\n"
157  " Context manager to ensure balanced bind calls, even in the case of an error.\n");
158 static PyObject *pygpu_offscreen_bind(BPyGPUOffScreen *self)
159 {
162  ret->py_offscreen = self;
163  ret->level = -1;
164  ret->is_explicitly_bound = false;
165  Py_INCREF(self);
166 
168  ret->is_explicitly_bound = true;
169 
170  return (PyObject *)ret;
171 }
172 
173 PyDoc_STRVAR(pygpu_offscreen_unbind_doc,
174  ".. method:: unbind(restore=True)\n"
175  "\n"
176  " Unbind the offscreen object.\n"
177  "\n"
178  " :arg restore: Restore the OpenGL state, can only be used when the state has been "
179  "saved before.\n"
180  " :type restore: bool\n");
181 static PyObject *pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
182 {
183  bool restore = true;
184 
186 
187  static const char *_keywords[] = {"restore", NULL};
188  static _PyArg_Parser _parser = {
189  "|$" /* Optional keyword only arguments. */
190  "O&" /* `restore` */
191  ":unbind",
192  _keywords,
193  0,
194  };
195  if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &restore)) {
196  return NULL;
197  }
198 
199  GPU_offscreen_unbind(self->ofs, restore);
200  GPU_apply_state();
201  Py_RETURN_NONE;
202 }
203 
206 /* -------------------------------------------------------------------- */
210 static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self),
211  PyObject *args,
212  PyObject *kwds)
213 {
215 
216  GPUOffScreen *ofs = NULL;
217  int width, height;
218  struct PyC_StringEnum pygpu_textureformat = {pygpu_framebuffer_color_texture_formats, GPU_RGBA8};
219  char err_out[256];
220 
221  static const char *_keywords[] = {"width", "height", "format", NULL};
222  static _PyArg_Parser _parser = {
223  "i" /* `width` */
224  "i" /* `height` */
225  "|$" /* Optional keyword only arguments. */
226  "O&" /* `format` */
227  ":GPUOffScreen.__new__",
228  _keywords,
229  0,
230  };
231  if (!_PyArg_ParseTupleAndKeywordsFast(
232  args, kwds, &_parser, &width, &height, PyC_ParseStringEnum, &pygpu_textureformat)) {
233  return NULL;
234  }
235 
236  if (GPU_context_active_get()) {
237  ofs = GPU_offscreen_create(width, height, true, pygpu_textureformat.value_found, err_out);
238  }
239  else {
240  STRNCPY(err_out, "No active GPU context found");
241  }
242 
243  if (ofs == NULL) {
244  PyErr_Format(PyExc_RuntimeError,
245  "gpu.offscreen.new(...) failed with '%s'",
246  err_out[0] ? err_out : "unknown error");
247  return NULL;
248  }
249 
250  return BPyGPUOffScreen_CreatePyObject(ofs);
251 }
252 
253 PyDoc_STRVAR(pygpu_offscreen_width_doc, "Width of the texture.\n\n:type: `int`");
254 static PyObject *pygpu_offscreen_width_get(BPyGPUOffScreen *self, void *UNUSED(type))
255 {
257  return PyLong_FromLong(GPU_offscreen_width(self->ofs));
258 }
259 
260 PyDoc_STRVAR(pygpu_offscreen_height_doc, "Height of the texture.\n\n:type: `int`");
261 static PyObject *pygpu_offscreen_height_get(BPyGPUOffScreen *self, void *UNUSED(type))
262 {
264  return PyLong_FromLong(GPU_offscreen_height(self->ofs));
265 }
266 
267 PyDoc_STRVAR(pygpu_offscreen_color_texture_doc,
268  "OpenGL bindcode for the color texture.\n\n:type: `int`");
270 {
273  return PyLong_FromLong(GPU_texture_opengl_bindcode(texture));
274 }
275 
276 PyDoc_STRVAR(pygpu_offscreen_texture_color_doc,
277  "The color texture attached.\n"
278  "\n"
279  ":type: :class:`gpu.types.GPUTexture`");
281 {
284  return BPyGPUTexture_CreatePyObject(texture, true);
285 }
286 
288  pygpu_offscreen_draw_view3d_doc,
289  ".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, "
290  "do_color_management=False)\n"
291  "\n"
292  " Draw the 3d viewport in the offscreen object.\n"
293  "\n"
294  " :arg scene: Scene to draw.\n"
295  " :type scene: :class:`bpy.types.Scene`\n"
296  " :arg view_layer: View layer to draw.\n"
297  " :type view_layer: :class:`bpy.types.ViewLayer`\n"
298  " :arg view3d: 3D View to get the drawing settings from.\n"
299  " :type view3d: :class:`bpy.types.SpaceView3D`\n"
300  " :arg region: Region of the 3D View (required as temporary draw target).\n"
301  " :type region: :class:`bpy.types.Region`\n"
302  " :arg view_matrix: View Matrix (e.g. ``camera.matrix_world.inverted()``).\n"
303  " :type view_matrix: :class:`mathutils.Matrix`\n"
304  " :arg projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n"
305  " :type projection_matrix: :class:`mathutils.Matrix`\n"
306  " :arg do_color_management: Color manage the output.\n"
307  " :type do_color_management: bool\n");
308 static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
309 {
310  MatrixObject *py_mat_view, *py_mat_projection;
311  PyObject *py_scene, *py_view_layer, *py_region, *py_view3d;
312 
313  struct Depsgraph *depsgraph;
314  struct Scene *scene;
315  struct ViewLayer *view_layer;
316  View3D *v3d;
317  ARegion *region;
318 
319  bool do_color_management = false;
320 
322 
323  static const char *_keywords[] = {
324  "scene",
325  "view_layer",
326  "view3d",
327  "region",
328  "view_matrix",
329  "projection_matrix",
330  "do_color_management",
331  NULL,
332  };
333  static _PyArg_Parser _parser = {
334  "O" /* `scene` */
335  "O" /* `view_layer` */
336  "O" /* `view3d` */
337  "O" /* `region` */
338  "O&" /* `view_matrix` */
339  "O&" /* `projection_matrix` */
340  "|$" /* Optional keyword only arguments. */
341  "O&" /* `do_color_management` */
342  ":draw_view3d",
343  _keywords,
344  0,
345  };
346  if (!_PyArg_ParseTupleAndKeywordsFast(args,
347  kwds,
348  &_parser,
349  &py_scene,
350  &py_view_layer,
351  &py_view3d,
352  &py_region,
354  &py_mat_view,
356  &py_mat_projection,
358  &do_color_management) ||
359  (!(scene = PyC_RNA_AsPointer(py_scene, "Scene")) ||
360  !(view_layer = PyC_RNA_AsPointer(py_view_layer, "ViewLayer")) ||
361  !(v3d = PyC_RNA_AsPointer(py_view3d, "SpaceView3D")) ||
362  !(region = PyC_RNA_AsPointer(py_region, "Region")))) {
363  return NULL;
364  }
365 
367 
369 
370  /* Disable 'bgl' state since it interfere with off-screen drawing, see: T84402. */
371  const bool is_bgl = GPU_bgl_get();
372  if (is_bgl) {
373  GPU_bgl_end();
374  }
375 
376  GPU_offscreen_bind(self->ofs, true);
377 
378  /* Cache the #GPUViewport so the frame-buffers and associated textures are
379  * not reallocated each time, see: T89204 */
380  if (!self->viewport) {
381  self->viewport = GPU_viewport_create();
382  }
383  else {
384  GPU_viewport_tag_update(self->viewport);
385  }
386 
388  scene,
389  v3d->shading.type,
390  v3d,
391  region,
394  (const float(*)[4])py_mat_view->matrix,
395  (const float(*)[4])py_mat_projection->matrix,
396  true,
397  true,
398  "",
399  do_color_management,
400  true,
401  self->ofs,
402  self->viewport);
403 
404  GPU_offscreen_unbind(self->ofs, true);
405 
406  if (is_bgl) {
407  GPU_bgl_start();
408  }
409 
410  Py_RETURN_NONE;
411 }
412 
413 #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
414 PyDoc_STRVAR(pygpu_offscreen_free_doc,
415  ".. method:: free()\n"
416  "\n"
417  " Free the offscreen object.\n"
418  " The framebuffer, texture and render objects will no longer be accessible.\n");
419 static PyObject *pygpu_offscreen_free(BPyGPUOffScreen *self)
420 {
422 
423  if (self->viewport) {
424  GPU_viewport_free(self->viewport);
425  self->viewport = NULL;
426  }
427 
428  GPU_offscreen_free(self->ofs);
429  self->ofs = NULL;
430  Py_RETURN_NONE;
431 }
432 #endif
433 
435 {
436  if (self->viewport) {
437  GPU_viewport_free(self->viewport);
438  }
439  if (self->ofs) {
440  GPU_offscreen_free(self->ofs);
441  }
442  Py_TYPE(self)->tp_free((PyObject *)self);
443 }
444 
445 static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
446  {"color_texture",
448  (setter)NULL,
449  pygpu_offscreen_color_texture_doc,
450  NULL},
451  {"texture_color",
453  (setter)NULL,
454  pygpu_offscreen_texture_color_doc,
455  NULL},
456  {"width", (getter)pygpu_offscreen_width_get, (setter)NULL, pygpu_offscreen_width_doc, NULL},
457  {"height", (getter)pygpu_offscreen_height_get, (setter)NULL, pygpu_offscreen_height_doc, NULL},
458  {NULL, NULL, NULL, NULL, NULL} /* Sentinel */
459 };
460 
461 static struct PyMethodDef pygpu_offscreen__tp_methods[] = {
462  {"bind", (PyCFunction)pygpu_offscreen_bind, METH_NOARGS, pygpu_offscreen_bind_doc},
463  {"unbind",
464  (PyCFunction)pygpu_offscreen_unbind,
465  METH_VARARGS | METH_KEYWORDS,
466  pygpu_offscreen_unbind_doc},
467  {"draw_view3d",
468  (PyCFunction)pygpu_offscreen_draw_view3d,
469  METH_VARARGS | METH_KEYWORDS,
470  pygpu_offscreen_draw_view3d_doc},
471 #ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
472  {"free", (PyCFunction)pygpu_offscreen_free, METH_NOARGS, pygpu_offscreen_free_doc},
473 #endif
474  {NULL, NULL, 0, NULL},
475 };
476 
477 PyDoc_STRVAR(pygpu_offscreen__tp_doc,
478  ".. class:: GPUOffScreen(width, height, *, format='RGBA8')\n"
479  "\n"
480  " This object gives access to off screen buffers.\n"
481  "\n"
482  " :arg width: Horizontal dimension of the buffer.\n"
483  " :type width: int\n"
484  " :arg height: Vertical dimension of the buffer.\n"
485  " :type height: int\n"
486  " :arg format: Internal data format inside GPU memory for color attachment "
487  "texture. Possible values are:\n"
488  " `RGBA8`,\n"
489  " `RGBA16`,\n"
490  " `RGBA16F`,\n"
491  " `RGBA32F`,\n"
492  " :type format: str\n");
493 PyTypeObject BPyGPUOffScreen_Type = {
494  PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUOffScreen",
495  .tp_basicsize = sizeof(BPyGPUOffScreen),
496  .tp_dealloc = (destructor)BPyGPUOffScreen__tp_dealloc,
497  .tp_flags = Py_TPFLAGS_DEFAULT,
498  .tp_doc = pygpu_offscreen__tp_doc,
499  .tp_methods = pygpu_offscreen__tp_methods,
500  .tp_getset = pygpu_offscreen__tp_getseters,
501  .tp_new = pygpu_offscreen__tp_new,
502 };
503 
506 /* -------------------------------------------------------------------- */
511 {
512  BPyGPUOffScreen *self;
513 
514  self = PyObject_New(BPyGPUOffScreen, &BPyGPUOffScreen_Type);
515  self->ofs = ofs;
516  self->viewport = NULL;
517 
518  return (PyObject *)self;
519 }
520 
523 #undef BPY_GPU_OFFSCREEN_CHECK_OBJ
#define G_MAIN
Definition: BKE_global.h:267
bool BKE_id_is_in_global_main(struct ID *id)
Definition: lib_id.c:1902
struct Depsgraph * BKE_scene_ensure_depsgraph(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer)
Definition: scene.cc:3456
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define STRNCPY(dst, src)
Definition: BLI_string.h:483
#define UNUSED(x)
#define UNLIKELY(x)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:35
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, bool do_color_management, bool restore_rv3d_mats, struct GPUOffScreen *ofs, struct GPUViewport *viewport)
GPUContext * GPU_context_active_get(void)
Definition: gpu_context.cc:142
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_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 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
void GPU_bgl_end(void)
Definition: gpu_state.cc:346
void GPU_bgl_start(void)
Definition: gpu_state.cc:316
void GPU_apply_state(void)
Definition: gpu_state.cc:301
bool GPU_bgl_get(void)
Definition: gpu_state.cc:360
struct GPUTexture GPUTexture
Definition: GPU_texture.h:17
int GPU_texture_opengl_bindcode(const GPUTexture *tex)
Definition: gpu_texture.cc:683
@ GPU_RGBA32F
Definition: GPU_texture.h:90
@ GPU_RGBA16
Definition: GPU_texture.h:94
@ GPU_RGBA8
Definition: GPU_texture.h:87
GPUViewport * GPU_viewport_create(void)
Definition: gpu_viewport.c:98
void GPU_viewport_free(GPUViewport *viewport)
Definition: gpu_viewport.c:585
void GPU_viewport_tag_update(GPUViewport *viewport)
Definition: gpu_viewport.c:86
PyObject * self
Definition: bpy_driver.c:165
Scene scene
const Depsgraph * depsgraph
depth_tx normal_tx diffuse_light_tx specular_light_tx volume_light_tx environment_tx ambient_occlusion_tx aov_value_tx in_weight_img GPU_RGBA16F
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, eGPUTextureFormat format, char err_out[256])
uint GPU_framebuffer_stack_level_get()
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:14
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_texture_color_get(BPyGPUOffScreen *self, void *UNUSED(type))
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 const struct PyC_StringEnumItems pygpu_framebuffer_color_texture_formats[]
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
PyObject * BPyGPUTexture_CreatePyObject(GPUTexture *tex, bool shared_reference)
int Matrix_Parse4x4(PyObject *o, void *p)
int PyC_ParseStringEnum(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