Python GPU: Add reference of PyObject GPU object to the GPU object itself

Instead of creating different python wrappers for the same GPU object,
return the same `PyObject` created earlier.

This also allows for more secure access to existing GPU objects.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D11044
This commit is contained in:
Germano Cavalcante
2021-04-29 14:48:59 -03:00
committed by Germano Cavalcante
parent c96506d54a
commit 04b6296e81
12 changed files with 143 additions and 25 deletions

View File

@@ -24,6 +24,7 @@
#pragma once #pragma once
#define PROGRAM_NO_OPTI 0 #define PROGRAM_NO_OPTI 0
#define USE_PY_REFERENCES 1
#if defined(NDEBUG) #if defined(NDEBUG)
# define TRUST_NO_ONE 0 # define TRUST_NO_ONE 0

View File

@@ -209,6 +209,11 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb,
void (*callback)(void *userData, int level), void (*callback)(void *userData, int level),
void *userData); void *userData);
#if USE_PY_REFERENCES
void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb);
void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref);
#endif
void GPU_framebuffer_push(GPUFrameBuffer *fb); void GPU_framebuffer_push(GPUFrameBuffer *fb);
GPUFrameBuffer *GPU_framebuffer_pop(void); GPUFrameBuffer *GPU_framebuffer_pop(void);
uint GPU_framebuffer_stack_level_get(void); uint GPU_framebuffer_stack_level_get(void);

View File

@@ -269,6 +269,12 @@ bool GPU_texture_cube(const GPUTexture *tex);
bool GPU_texture_depth(const GPUTexture *tex); bool GPU_texture_depth(const GPUTexture *tex);
bool GPU_texture_stencil(const GPUTexture *tex); bool GPU_texture_stencil(const GPUTexture *tex);
bool GPU_texture_integer(const GPUTexture *tex); bool GPU_texture_integer(const GPUTexture *tex);
#if USE_PY_REFERENCES
void **GPU_texture_py_reference_get(GPUTexture *tex);
void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref);
#endif
int GPU_texture_opengl_bindcode(const GPUTexture *tex); int GPU_texture_opengl_bindcode(const GPUTexture *tex);
void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size); void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size);

View File

@@ -71,6 +71,12 @@ FrameBuffer::~FrameBuffer()
reinterpret_cast<Texture *>(attachment.tex)->detach_from(this); reinterpret_cast<Texture *>(attachment.tex)->detach_from(this);
} }
} }
#if USE_PY_REFERENCES
if (this->py_ref) {
*this->py_ref = nullptr;
}
#endif
} }
/** \} */ /** \} */
@@ -473,6 +479,19 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb,
unwrap(gpu_fb)->recursive_downsample(max_lvl, callback, userData); unwrap(gpu_fb)->recursive_downsample(max_lvl, callback, userData);
} }
#if USE_PY_REFERENCES
void **GPU_framebuffer_py_reference_get(GPUFrameBuffer *gpu_fb)
{
return unwrap(gpu_fb)->py_ref;
}
void GPU_framebuffer_py_reference_set(GPUFrameBuffer *gpu_fb, void **py_ref)
{
BLI_assert(ref == nullptr || unwrap(gpu_fb)->py_ref == nullptr);
unwrap(gpu_fb)->py_ref = py_ref;
}
#endif
/** \} */ /** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */

View File

@@ -100,6 +100,15 @@ class FrameBuffer {
bool scissor_test_ = false; bool scissor_test_ = false;
bool dirty_state_ = true; bool dirty_state_ = true;
#if USE_PY_REFERENCES
public:
/**
* Reference of a pointer that needs to be cleaned when deallocating the frame-buffer.
* Points to #BPyGPUFrameBuffer.fb
*/
void **py_ref = nullptr;
#endif
public: public:
FrameBuffer(const char *name); FrameBuffer(const char *name);
virtual ~FrameBuffer(); virtual ~FrameBuffer();

View File

@@ -60,6 +60,12 @@ Texture::~Texture()
fb_[i]->attachment_remove(fb_attachment_[i]); fb_[i]->attachment_remove(fb_attachment_[i]);
} }
} }
#if USE_PY_REFERENCES
if (this->py_ref) {
*this->py_ref = nullptr;
}
#endif
} }
bool Texture::init_1D(int w, int layers, eGPUTextureFormat format) bool Texture::init_1D(int w, int layers, eGPUTextureFormat format)
@@ -581,6 +587,19 @@ bool GPU_texture_array(const GPUTexture *tex)
return (reinterpret_cast<const Texture *>(tex)->type_get() & GPU_TEXTURE_ARRAY) != 0; return (reinterpret_cast<const Texture *>(tex)->type_get() & GPU_TEXTURE_ARRAY) != 0;
} }
#if USE_PY_REFERENCES
void **GPU_texture_py_reference_get(GPUTexture *tex)
{
return unwrap(tex)->py_ref;
}
void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref)
{
BLI_assert(py_ref == nullptr || unwrap(tex)->py_ref == nullptr);
unwrap(tex)->py_ref = py_ref;
}
#endif
/* TODO remove */ /* TODO remove */
int GPU_texture_opengl_bindcode(const GPUTexture *tex) int GPU_texture_opengl_bindcode(const GPUTexture *tex)
{ {

View File

@@ -80,6 +80,13 @@ class Texture {
int refcount = 1; int refcount = 1;
/** Width & Height (of source data), optional. */ /** Width & Height (of source data), optional. */
int src_w = 0, src_h = 0; int src_w = 0, src_h = 0;
#if USE_PY_REFERENCES
/**
* Reference of a pointer that needs to be cleaned when deallocating the texture.
* Points to #BPyGPUTexture.tex
*/
void **py_ref = nullptr;
#endif
protected: protected:
/* ---- Texture format (immutable after init). ---- */ /* ---- Texture format (immutable after init). ---- */

View File

@@ -46,13 +46,7 @@
static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb) static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
{ {
if (UNLIKELY(bpygpu_fb->fb == NULL)) { if (UNLIKELY(bpygpu_fb->fb == NULL)) {
PyErr_SetString(PyExc_ReferenceError, PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid");
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
"GPU framebuffer was freed, no further access is valid"
#else
"GPU framebuffer: internal error"
#endif
);
return -1; return -1;
} }
return 0; return 0;
@@ -68,10 +62,6 @@ static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb) static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
{ {
if (!fb) {
return;
}
if (GPU_is_init()) { if (GPU_is_init()) {
GPU_framebuffer_free(fb); GPU_framebuffer_free(fb);
} }
@@ -80,6 +70,21 @@ static void pygpu_framebuffer_free_if_possible(GPUFrameBuffer *fb)
} }
} }
static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self)
{
if (self->fb) {
#if GPU_USE_PY_REFERENCES
GPU_framebuffer_py_reference_set(self->fb, NULL);
if (!self->shared_reference)
#endif
{
pygpu_framebuffer_free_if_possible(self->fb);
}
self->fb = NULL;
}
}
/* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */ /* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */
#define GPU_PY_FRAMEBUFFER_STACK_LEN 16 #define GPU_PY_FRAMEBUFFER_STACK_LEN 16
@@ -336,7 +341,7 @@ static PyObject *pygpu_framebuffer__tp_new(PyTypeObject *UNUSED(self),
GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python"); GPUFrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1); GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
return BPyGPUFrameBuffer_CreatePyObject(fb_python); return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
} }
PyDoc_STRVAR(pygpu_framebuffer_is_bound_doc, PyDoc_STRVAR(pygpu_framebuffer_is_bound_doc,
@@ -459,15 +464,14 @@ PyDoc_STRVAR(pygpu_framebuffer_free_doc,
static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self) static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
{ {
PYGPU_FRAMEBUFFER_CHECK_OBJ(self); PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
pygpu_framebuffer_free_if_possible(self->fb); pygpu_framebuffer_free_safe(self);
self->fb = NULL;
Py_RETURN_NONE; Py_RETURN_NONE;
} }
#endif #endif
static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self) static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self)
{ {
pygpu_framebuffer_free_if_possible(self->fb); pygpu_framebuffer_free_safe(self);
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free((PyObject *)self);
} }
@@ -531,13 +535,35 @@ PyTypeObject BPyGPUFrameBuffer_Type = {
/** \name Public API /** \name Public API
* \{ */ * \{ */
PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb) PyObject *BPyGPUFrameBuffer_CreatePyObject(GPUFrameBuffer *fb, bool shared_reference)
{ {
BPyGPUFrameBuffer *self; BPyGPUFrameBuffer *self;
#if GPU_USE_PY_REFERENCES
if (shared_reference) {
void **ref = GPU_framebuffer_py_reference_get(fb);
if (ref) {
/* Retrieve BPyGPUFrameBuffer reference. */
self = POINTER_OFFSET(ref, -offsetof(BPyGPUFrameBuffer, fb));
BLI_assert(self->fb == fb);
Py_INCREF(self);
return (PyObject *)self;
}
}
#else
UNUSED_VARS(shared_reference);
#endif
self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type); self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type);
self->fb = fb; self->fb = fb;
#if GPU_USE_PY_REFERENCES
self->shared_reference = shared_reference;
BLI_assert(GPU_framebuffer_py_reference_get(fb) == NULL);
GPU_framebuffer_py_reference_set(fb, &self->fb);
#endif
return (PyObject *)self; return (PyObject *)self;
} }

View File

@@ -28,6 +28,11 @@ extern PyTypeObject BPyGPUFrameBuffer_Type;
typedef struct BPyGPUFrameBuffer { typedef struct BPyGPUFrameBuffer {
PyObject_HEAD struct GPUFrameBuffer *fb; PyObject_HEAD struct GPUFrameBuffer *fb;
#if GPU_USE_PY_REFERENCES
bool shared_reference;
#endif
} BPyGPUFrameBuffer; } BPyGPUFrameBuffer;
PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb) ATTR_NONNULL(1); PyObject *BPyGPUFrameBuffer_CreatePyObject(struct GPUFrameBuffer *fb, bool shared_reference)
ATTR_NONNULL(1);

View File

@@ -247,7 +247,7 @@ static PyObject *pygpu_texture__tp_new(PyTypeObject *UNUSED(self), PyObject *arg
return NULL; return NULL;
} }
return BPyGPUTexture_CreatePyObject(tex); return BPyGPUTexture_CreatePyObject(tex, false);
} }
PyDoc_STRVAR(pygpu_texture_width_doc, "Width of the texture.\n\n:type: `int`"); PyDoc_STRVAR(pygpu_texture_width_doc, "Width of the texture.\n\n:type: `int`");
@@ -412,6 +412,9 @@ static PyObject *pygpu_texture_free(BPyGPUTexture *self)
static void BPyGPUTexture__tp_dealloc(BPyGPUTexture *self) static void BPyGPUTexture__tp_dealloc(BPyGPUTexture *self)
{ {
if (self->tex) { if (self->tex) {
#if GPU_USE_PY_REFERENCES
GPU_texture_py_reference_set(self->tex, NULL);
#endif
GPU_texture_free(self->tex); GPU_texture_free(self->tex);
} }
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free((PyObject *)self);
@@ -535,10 +538,7 @@ static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg)
BKE_imageuser_default(&iuser); BKE_imageuser_default(&iuser);
GPUTexture *tex = BKE_image_get_gpu_texture(ima, &iuser, NULL); GPUTexture *tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
/* Increase the texture reference count. */ return BPyGPUTexture_CreatePyObject(tex, true);
GPU_texture_ref(tex);
return BPyGPUTexture_CreatePyObject(tex);
} }
static struct PyMethodDef pygpu_texture__m_methods[] = { static struct PyMethodDef pygpu_texture__m_methods[] = {
@@ -595,13 +595,33 @@ PyObject *bpygpu_texture_init(void)
/** \name Public API /** \name Public API
* \{ */ * \{ */
PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex) PyObject *BPyGPUTexture_CreatePyObject(GPUTexture *tex, bool shared_reference)
{ {
BPyGPUTexture *self; BPyGPUTexture *self;
if (shared_reference) {
#if GPU_USE_PY_REFERENCES
void **ref = GPU_texture_py_reference_get(tex);
if (ref) {
/* Retrieve BPyGPUTexture reference. */
self = POINTER_OFFSET(ref, -offsetof(BPyGPUTexture, tex));
BLI_assert(self->tex == tex);
Py_INCREF(self);
return (PyObject *)self;
}
#endif
GPU_texture_ref(tex);
}
self = PyObject_New(BPyGPUTexture, &BPyGPUTexture_Type); self = PyObject_New(BPyGPUTexture, &BPyGPUTexture_Type);
self->tex = tex; self->tex = tex;
#if GPU_USE_PY_REFERENCES
BLI_assert(GPU_texture_py_reference_get(tex) == NULL);
GPU_texture_py_reference_set(tex, &self->tex);
#endif
return (PyObject *)self; return (PyObject *)self;
} }

View File

@@ -33,4 +33,5 @@ typedef struct BPyGPUTexture {
int bpygpu_ParseTexture(PyObject *o, void *p); int bpygpu_ParseTexture(PyObject *o, void *p);
PyObject *bpygpu_texture_init(void); PyObject *bpygpu_texture_init(void);
PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex) ATTR_NONNULL(1); PyObject *BPyGPUTexture_CreatePyObject(struct GPUTexture *tex, bool weak_reference)
ATTR_NONNULL(1);