Add Chromium-only Blender WebEngine parity work
This commit is contained in:
74
blender-5.2.0/source/blender/python/gpu/CMakeLists.txt
Normal file
74
blender-5.2.0/source/blender/python/gpu/CMakeLists.txt
Normal file
@@ -0,0 +1,74 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
.
|
||||
../../editors/include
|
||||
../../makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
gpu_py.cc
|
||||
gpu_py_api.cc
|
||||
gpu_py_batch.cc
|
||||
gpu_py_buffer.cc
|
||||
gpu_py_capabilities.cc
|
||||
gpu_py_compute.cc
|
||||
gpu_py_element.cc
|
||||
gpu_py_framebuffer.cc
|
||||
gpu_py_matrix.cc
|
||||
gpu_py_offscreen.cc
|
||||
gpu_py_platform.cc
|
||||
gpu_py_select.cc
|
||||
gpu_py_shader.cc
|
||||
gpu_py_shader_create_info.cc
|
||||
gpu_py_state.cc
|
||||
gpu_py_texture.cc
|
||||
gpu_py_types.cc
|
||||
gpu_py_uniformbuffer.cc
|
||||
gpu_py_vertex_buffer.cc
|
||||
gpu_py_vertex_format.cc
|
||||
|
||||
gpu_py.hh
|
||||
gpu_py_api.hh
|
||||
gpu_py_batch.hh
|
||||
gpu_py_buffer.hh
|
||||
gpu_py_capabilities.hh
|
||||
gpu_py_compute.hh
|
||||
gpu_py_element.hh
|
||||
gpu_py_framebuffer.hh
|
||||
gpu_py_matrix.hh
|
||||
gpu_py_offscreen.hh
|
||||
gpu_py_platform.hh
|
||||
gpu_py_select.hh
|
||||
gpu_py_shader.hh
|
||||
gpu_py_state.hh
|
||||
gpu_py_texture.hh
|
||||
gpu_py_types.hh
|
||||
gpu_py_uniformbuffer.hh
|
||||
gpu_py_vertex_buffer.hh
|
||||
gpu_py_vertex_format.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::windowmanager
|
||||
PRIVATE bf::dependencies::optional::python
|
||||
)
|
||||
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
list(APPEND LIB
|
||||
PRIVATE bf::dependencies::epoxy
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_python_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
72
blender-5.2.0/source/blender/python/gpu/gpu_py.cc
Normal file
72
blender-5.2.0/source/blender/python/gpu/gpu_py.cc
Normal file
@@ -0,0 +1,72 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_init_exit.hh"
|
||||
#include "GPU_primitive.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "gpu_py.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Enums
|
||||
* \{ */
|
||||
|
||||
PyC_StringEnumItems bpygpu_primtype_items[] = {
|
||||
{GPU_PRIM_POINTS, "POINTS"},
|
||||
{GPU_PRIM_LINES, "LINES"},
|
||||
{GPU_PRIM_TRIS, "TRIS"},
|
||||
{GPU_PRIM_LINE_STRIP, "LINE_STRIP"},
|
||||
{GPU_PRIM_LINE_LOOP, "LINE_LOOP"},
|
||||
{GPU_PRIM_TRI_STRIP, "TRI_STRIP"},
|
||||
{GPU_PRIM_TRI_FAN, "TRI_FAN"},
|
||||
{GPU_PRIM_LINES_ADJ, "LINES_ADJ"},
|
||||
{GPU_PRIM_TRIS_ADJ, "TRIS_ADJ"},
|
||||
{GPU_PRIM_LINE_STRIP_ADJ, "LINE_STRIP_ADJ"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
PyC_StringEnumItems bpygpu_dataformat_items[] = {
|
||||
{GPU_DATA_FLOAT, "FLOAT"},
|
||||
{GPU_DATA_INT, "INT"},
|
||||
{GPU_DATA_UINT, "UINT"},
|
||||
{GPU_DATA_UBYTE, "UBYTE"},
|
||||
{GPU_DATA_UINT_24_8_DEPRECATED, "UINT_24_8"},
|
||||
{GPU_DATA_10_11_11_REV, "10_11_11_REV"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
* \{ */
|
||||
|
||||
bool bpygpu_is_init_or_error()
|
||||
{
|
||||
if (!GPU_is_init()) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"GPU functions for drawing requires the gpu module to be initialized. See gpu.init.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
56
blender-5.2.0/source/blender/python/gpu/gpu_py.hh
Normal file
56
blender-5.2.0/source/blender/python/gpu/gpu_py.hh
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern struct PyC_StringEnumItems bpygpu_primtype_items[];
|
||||
extern struct PyC_StringEnumItems bpygpu_dataformat_items[];
|
||||
|
||||
/* Docstring Literal types for shared enums. */
|
||||
|
||||
#define PYDOC_PRIMTYPE_LITERAL \
|
||||
"Literal[" \
|
||||
"'POINTS', " \
|
||||
"'LINES', " \
|
||||
"'TRIS', " \
|
||||
"'LINE_STRIP', " \
|
||||
"'LINE_LOOP', " \
|
||||
"'TRI_STRIP', " \
|
||||
"'TRI_FAN', " \
|
||||
"'LINES_ADJ', " \
|
||||
"'TRIS_ADJ', " \
|
||||
"'LINE_STRIP_ADJ']"
|
||||
#define PYDOC_DATAFORMAT_LITERAL \
|
||||
"Literal[" \
|
||||
"'FLOAT', " \
|
||||
"'INT', " \
|
||||
"'UINT', " \
|
||||
"'UBYTE', " \
|
||||
"'UINT_24_8', " \
|
||||
"'10_11_11_REV']"
|
||||
|
||||
[[nodiscard]] bool bpygpu_is_init_or_error();
|
||||
|
||||
#define BPYGPU_IS_INIT_OR_ERROR_OBJ \
|
||||
if (UNLIKELY(!bpygpu_is_init_or_error())) { \
|
||||
return NULL; \
|
||||
} \
|
||||
((void)0)
|
||||
#define BPYGPU_IS_INIT_OR_ERROR_INT \
|
||||
if (UNLIKELY(!bpygpu_is_init_or_error())) { \
|
||||
return -1; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
} // namespace blender
|
||||
151
blender-5.2.0/source/blender/python/gpu/gpu_py_api.cc
Normal file
151
blender-5.2.0/source/blender/python/gpu/gpu_py_api.cc
Normal file
@@ -0,0 +1,151 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* Experimental Python API, not considered public yet (called '_gpu'),
|
||||
* we may re-expose as public later.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "gpu_py_capabilities.hh"
|
||||
#include "gpu_py_compute.hh"
|
||||
#include "gpu_py_matrix.hh"
|
||||
#include "gpu_py_platform.hh"
|
||||
#include "gpu_py_select.hh"
|
||||
#include "gpu_py_state.hh"
|
||||
#include "gpu_py_types.hh"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_init_exit.hh"
|
||||
#include "WM_api.hh"
|
||||
#include "gpu_py_api.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Module
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_init_doc,
|
||||
".. function:: init()\n"
|
||||
"\n"
|
||||
" Initializes the GPU module for background use.\n"
|
||||
" If the initialization fails, a SystemError will be raised.\n");
|
||||
|
||||
static PyObject *pygpu_init(PyObject * /*self*/)
|
||||
{
|
||||
if (!G.background || GPU_is_init()) {
|
||||
/* GPU is already initialized. */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
if (!GPU_backend_supported()) {
|
||||
PyErr_SetString(PyExc_SystemError, "Failed to initialize GPU. GPU backend not supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Cannot use GPU_init() as it requires a GPU context to have been created.
|
||||
* See WM_init_gpu implementation. */
|
||||
WM_init_gpu();
|
||||
|
||||
if (!GPU_is_init()) {
|
||||
PyErr_SetString(PyExc_SystemError, "Failed to initialize GPU. Unexpected Error");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_tp_methods[] = {
|
||||
{"init", reinterpret_cast<PyCFunction>(pygpu_init), METH_NOARGS, pygpu_init_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_doc,
|
||||
"This module provides Python wrappers for the GPU implementation in Blender.\n"
|
||||
"Some higher level functions can be found in the :mod:`gpu_extras` module.\n");
|
||||
static PyModuleDef pygpu_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu",
|
||||
/*m_doc*/ pygpu_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPyInit_gpu()
|
||||
{
|
||||
PyObject *sys_modules = PyImport_GetModuleDict();
|
||||
PyObject *submodule;
|
||||
PyObject *mod;
|
||||
|
||||
mod = PyModule_Create(&pygpu_module_def);
|
||||
|
||||
PyModule_AddObject(mod, "types", (submodule = bpygpu_types_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "capabilities", (submodule = bpygpu_capabilities_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "matrix", (submodule = bpygpu_matrix_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "platform", (submodule = bpygpu_platform_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "select", (submodule = bpygpu_select_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "shader", (submodule = bpygpu_shader_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "state", (submodule = bpygpu_state_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "texture", (submodule = bpygpu_texture_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "compute", (submodule = bpygpu_compute_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
21
blender-5.2.0/source/blender/python/gpu/gpu_py_api.hh
Normal file
21
blender-5.2.0/source/blender/python/gpu/gpu_py_api.hh
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Each type object could have a method for free GPU resources.
|
||||
* However, it is currently of little use. */
|
||||
// #define BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_gpu();
|
||||
|
||||
} // namespace blender
|
||||
708
blender-5.2.0/source/blender/python/gpu/gpu_py_batch.cc
Normal file
708
blender-5.2.0/source/blender/python/gpu/gpu_py_batch.cc
Normal file
@@ -0,0 +1,708 @@
|
||||
/* SPDX-FileCopyrightText: 2015 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the off-screen 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 "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_element.hh"
|
||||
#include "gpu_py_shader.hh"
|
||||
#include "gpu_py_vertex_buffer.hh"
|
||||
|
||||
#include "gpu_py_batch.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility Functions
|
||||
* \{ */
|
||||
|
||||
static bool pygpu_batch_is_program_or_error(BPyGPUBatch *self)
|
||||
{
|
||||
if (!self->batch->shader) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "batch does not have any program assigned to it");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUBatch Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_batch__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
const char *exc_str_missing_arg = "GPUBatch.__new__() missing required argument '%s' (pos %d)";
|
||||
|
||||
PyC_StringEnum prim_type = {bpygpu_primtype_items, GPU_PRIM_NONE};
|
||||
BPyGPUVertBuf *py_vertbuf = nullptr;
|
||||
BPyGPUIndexBuf *py_indexbuf = nullptr;
|
||||
PyC_TypeOrNone py_indexbuf_or_none = PyC_TYPE_OR_NONE_INIT(&BPyGPUIndexBuf_Type, &py_indexbuf);
|
||||
|
||||
static const char *_keywords[] = {"type", "buf", "elem", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `type` */
|
||||
"O!" /* `buf` */
|
||||
"O&" /* `elem` */
|
||||
":GPUBatch.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kwds,
|
||||
&_parser,
|
||||
PyC_ParseStringEnum,
|
||||
&prim_type,
|
||||
&BPyGPUVertBuf_Type,
|
||||
&py_vertbuf,
|
||||
PyC_ParseTypeOrNone,
|
||||
&py_indexbuf_or_none))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(prim_type.value_found != GPU_PRIM_NONE);
|
||||
if (prim_type.value_found == GPU_PRIM_LINE_LOOP) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"'LINE_LOOP' is deprecated. Please use 'LINE_STRIP' and close the segment.",
|
||||
1);
|
||||
}
|
||||
else if (prim_type.value_found == GPU_PRIM_TRI_FAN) {
|
||||
PyErr_WarnEx(
|
||||
PyExc_DeprecationWarning,
|
||||
"'TRI_FAN' is deprecated. Please use 'TRI_STRIP' or 'TRIS' and try modifying your "
|
||||
"vertices or indices to match the topology.",
|
||||
1);
|
||||
}
|
||||
|
||||
if (py_vertbuf == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError, exc_str_missing_arg, _keywords[1], 2);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Batch *batch = GPU_batch_create(GPUPrimType(prim_type.value_found),
|
||||
py_vertbuf->buf,
|
||||
py_indexbuf ? py_indexbuf->elem : nullptr);
|
||||
|
||||
BPyGPUBatch *ret = reinterpret_cast<BPyGPUBatch *>(BPyGPUBatch_CreatePyObject(batch));
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
ret->references = PyList_New(py_indexbuf ? 2 : 1);
|
||||
PyList_SET_ITEM(ret->references, 0, (PyObject *)py_vertbuf);
|
||||
Py_INCREF(py_vertbuf);
|
||||
|
||||
if (py_indexbuf != nullptr) {
|
||||
PyList_SET_ITEM(ret->references, 1, (PyObject *)py_indexbuf);
|
||||
Py_INCREF(py_indexbuf);
|
||||
}
|
||||
|
||||
BLI_assert(!PyObject_GC_IsTracked((PyObject *)ret));
|
||||
PyObject_GC_Track(ret);
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_vertbuf_add_doc, ".. method:: vertbuf_add(buf)\n"
|
||||
"\n"
|
||||
" Add another vertex buffer to the Batch.\n"
|
||||
" It is not possible to add more vertices to the batch using this method.\n"
|
||||
" Instead it can be used to add more attributes to the existing vertices.\n"
|
||||
" A good use case would be when you have a separate\n"
|
||||
" vertex buffer for vertex positions and vertex normals.\n"
|
||||
" Current a batch can have at most " STRINGIFY(GPU_BATCH_VBO_MAX_LEN) " vertex buffers.\n"
|
||||
"\n"
|
||||
" :param buf: The vertex buffer that will be added to the batch.\n"
|
||||
" :type buf: :class:`gpu.types.GPUVertBuf`\n"
|
||||
);
|
||||
static PyObject *pygpu_batch_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 nullptr;
|
||||
}
|
||||
|
||||
if (GPU_vertbuf_get_vertex_len(self->batch->verts[0]) != GPU_vertbuf_get_vertex_len(py_buf->buf))
|
||||
{
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Expected %d length, got %d",
|
||||
GPU_vertbuf_get_vertex_len(self->batch->verts[0]),
|
||||
GPU_vertbuf_get_vertex_len(py_buf->buf));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (self->batch->verts[GPU_BATCH_VBO_MAX_LEN - 1] != nullptr) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"Maximum number of vertex buffers exceeded: " STRINGIFY(GPU_BATCH_VBO_MAX_LEN));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Hold user */
|
||||
PyList_Append(self->references, reinterpret_cast<PyObject *>(py_buf));
|
||||
#endif
|
||||
|
||||
GPU_batch_vertbuf_add(self->batch, py_buf->buf, false);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_program_set_doc,
|
||||
".. method:: program_set(program)\n"
|
||||
"\n"
|
||||
" Assign a shader to this batch that will be used for drawing when not overwritten later.\n"
|
||||
" Note: This method has to be called in the draw context that the batch will be drawn in.\n"
|
||||
" This function does not need to be called when you always\n"
|
||||
" set the shader when calling :meth:`gpu.types.GPUBatch.draw`.\n"
|
||||
"\n"
|
||||
" :param program: The program/shader the batch will use in future draw calls.\n"
|
||||
" :type program: :class:`gpu.types.GPUShader`\n");
|
||||
static PyObject *pygpu_batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader)
|
||||
{
|
||||
static bool deprecation_warning_issued = false;
|
||||
|
||||
/* Deprecation warning raised when calling `gpu.types.GPUBatch.program_set`. */
|
||||
if (!deprecation_warning_issued) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calls to GPUBatch.program_set are deprecated."
|
||||
"Please set the shader via the 'program' parameter when calling "
|
||||
"GPUBatch.draw/draw_instanced/draw_range.",
|
||||
1);
|
||||
deprecation_warning_issued = true;
|
||||
}
|
||||
|
||||
if (!BPyGPUShader_Check(py_shader)) {
|
||||
PyErr_Format(PyExc_TypeError, "Expected a GPUShader, got %s", Py_TYPE(py_shader)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Shader *shader = py_shader->shader;
|
||||
GPU_batch_set_shader(self->batch, shader);
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Remove existing user (if any), hold new user. */
|
||||
int i = PyList_GET_SIZE(self->references);
|
||||
while (--i != -1) {
|
||||
PyObject *py_shader_test = PyList_GET_ITEM(self->references, i);
|
||||
if (BPyGPUShader_Check(py_shader_test)) {
|
||||
PyList_SET_ITEM(self->references, i, (PyObject *)py_shader);
|
||||
Py_INCREF(py_shader);
|
||||
Py_DECREF(py_shader_test);
|
||||
/* Only ever reference one shader. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == -1) {
|
||||
/* No references set in the loop, so add it here. */
|
||||
PyList_Append(self->references, reinterpret_cast<PyObject *>(py_shader));
|
||||
}
|
||||
#endif
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the Shader is compatible with the batch and can be used for rendering.
|
||||
* Derived from `polyline_draw_workaround` in `gpu_immediate.cc`.
|
||||
*/
|
||||
static const char *pygpu_shader_check_compatibility(gpu::Batch *batch)
|
||||
{
|
||||
if (!batch->shader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Currently only POLYLINE shaders are checked. */
|
||||
if (!bpygpu_shader_is_polyline(batch->shader)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check batch compatibility with shader. */
|
||||
for (auto *vert : Span(batch->verts, ARRAY_SIZE(batch->verts))) {
|
||||
if (!vert) {
|
||||
continue;
|
||||
}
|
||||
GPUVertFormat &format = vert->format;
|
||||
if ((format.stride % 4) != 0) {
|
||||
return "For POLYLINE shaders, only 4-byte aligned formats are supported";
|
||||
}
|
||||
|
||||
int pos_attr_id = -1;
|
||||
int col_attr_id = -1;
|
||||
for (uint a_idx = 0; a_idx < format.attr_len; a_idx++) {
|
||||
const GPUVertAttr *a = &format.attrs[a_idx];
|
||||
if ((a->offset % 4) != 0) {
|
||||
return "For POLYLINE shaders, only 4-byte aligned attributes are supported";
|
||||
}
|
||||
const StringRefNull name = GPU_vertformat_attr_name_get(&format, a, 0);
|
||||
if (pos_attr_id == -1 && name == "pos") {
|
||||
if (!ELEM(a->type.comp_type(), GPU_COMP_F32)) {
|
||||
return "For POLYLINE shaders, the 'pos' attribute needs to be 'F32'";
|
||||
}
|
||||
if (!ELEM(a->type.fetch_mode(), GPU_FETCH_FLOAT)) {
|
||||
return "For POLYLINE shaders, the 'pos' attribute must use the 'FLOAT' fetch type";
|
||||
}
|
||||
pos_attr_id = a_idx;
|
||||
}
|
||||
else if (col_attr_id == -1 && name == "color") {
|
||||
if (!ELEM(a->type.comp_type(), GPU_COMP_F32, GPU_COMP_U8)) {
|
||||
return "For POLYLINE shaders, the 'color' attribute needs to be 'F32' or 'U8'";
|
||||
}
|
||||
col_attr_id = a_idx;
|
||||
}
|
||||
if (pos_attr_id != -1 && col_attr_id != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_draw_doc,
|
||||
".. method:: draw(shader=None)\n"
|
||||
"\n"
|
||||
" Run the drawing shader with the parameters assigned to the batch.\n"
|
||||
"\n"
|
||||
" :param shader: Shader that performs the drawing operations.\n"
|
||||
" If ``None`` is passed, the last shader set to this batch will run.\n"
|
||||
" :type shader: :class:`gpu.types.GPUShader` | None\n");
|
||||
static PyObject *pygpu_batch_draw(BPyGPUBatch *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static bool deprecation_warning_issued = false;
|
||||
|
||||
BPyGPUShader *py_shader = nullptr;
|
||||
PyC_TypeOrNone py_shader_or_none = PyC_TYPE_OR_NONE_INIT(&BPyGPUShader_Type, &py_shader);
|
||||
|
||||
static const char *_keywords[] = {"shader", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|" /* Optional arguments. */
|
||||
"O&" /* `shader` */
|
||||
":GPUBatch.draw",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, PyC_ParseTypeOrNone, &py_shader_or_none))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (py_shader == nullptr) {
|
||||
if (!deprecation_warning_issued) {
|
||||
/* Deprecation warning raised when calling gpu.types.GPUBatch.draw without a valid GPUShader.
|
||||
*/
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw without specifying a shader is deprecated. "
|
||||
"Please provide a valid GPUShader as the 'shader' parameter.",
|
||||
1);
|
||||
deprecation_warning_issued = true;
|
||||
}
|
||||
|
||||
if (!pygpu_batch_is_program_or_error(self)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else if (self->batch->shader != py_shader->shader) {
|
||||
GPU_batch_set_shader(self->batch, py_shader->shader);
|
||||
}
|
||||
|
||||
/* Emit a warning when trying to draw wide lines as it is too late to automatically switch to a
|
||||
* polyline shader. */
|
||||
if (py_shader && py_shader->is_builtin &&
|
||||
ELEM(self->batch->prim_type, GPU_PRIM_LINES, GPU_PRIM_LINE_STRIP, GPU_PRIM_LINE_LOOP))
|
||||
{
|
||||
gpu::Shader *shader = py_shader->shader;
|
||||
const float line_width = GPU_line_width_get();
|
||||
const bool use_linesmooth = GPU_line_smooth_get();
|
||||
if (line_width > 1.0f || use_linesmooth) {
|
||||
if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw wide or smooth lines with "
|
||||
"GPU_SHADER_3D_FLAT_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POLYLINE_FLAT_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw wide or smooth lines with "
|
||||
"GPU_SHADER_3D_SMOOTH_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_UNIFORM_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw wide or smooth lines with "
|
||||
"GPU_SHADER_3D_UNIFORM_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit a warning when trying to draw points with a regular shader as it is too late to
|
||||
* automatically switch to a point shader. */
|
||||
if (py_shader && py_shader->is_builtin && self->batch->prim_type == GPU_PRIM_POINTS) {
|
||||
gpu::Shader *shader = py_shader->shader;
|
||||
if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw points with "
|
||||
"GPU_SHADER_3D_FLAT_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POINT_FLAT_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw points with "
|
||||
"GPU_SHADER_3D_SMOOTH_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POINT_FLAT_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_UNIFORM_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw points with "
|
||||
"GPU_SHADER_3D_UNIFORM_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POINT_SMOOTH_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
if (const char *error = pygpu_shader_check_compatibility(self->batch)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_batch_draw(self->batch);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_draw_instanced_doc,
|
||||
".. method:: draw_instanced(program, *, instance_start=0, instance_count=0)\n"
|
||||
"\n"
|
||||
" Draw multiple instances of the drawing program with the parameters assigned\n"
|
||||
" to the batch. In the vertex shader, ``gl_InstanceID`` will contain the instance\n"
|
||||
" number being drawn.\n"
|
||||
"\n"
|
||||
" :param program: Program that performs the drawing operations.\n"
|
||||
" :type program: :class:`gpu.types.GPUShader`\n"
|
||||
" :param instance_start: Number of the first instance to draw.\n"
|
||||
" :type instance_start: int\n"
|
||||
" :param instance_count: Number of instances to draw. When not provided or set to 0\n"
|
||||
" the number of instances will be determined by the number of rows in the first\n"
|
||||
" vertex buffer.\n"
|
||||
" :type instance_count: int\n");
|
||||
static PyObject *pygpu_batch_draw_instanced(BPyGPUBatch *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
BPyGPUShader *py_program = nullptr;
|
||||
int instance_start = 0;
|
||||
int instance_count = 0;
|
||||
|
||||
static const char *_keywords[] = {"program", "instance_start", "instance_count", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O!" /* `program` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"i" /* `instance_start` */
|
||||
"i" /* `instance_count` */
|
||||
":GPUBatch.draw_instanced",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, &BPyGPUShader_Type, &py_program, &instance_start, &instance_count))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_batch_set_shader(self->batch, py_program->shader);
|
||||
GPU_batch_draw_instance_range(self->batch, instance_start, instance_count);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_draw_range_doc,
|
||||
".. method:: draw_range(program, *, elem_start=0, elem_count=0)\n"
|
||||
"\n"
|
||||
" Run the drawing program with the parameters assigned to the batch. "
|
||||
"Only draw the ``elem_count`` elements of the index buffer starting at ``elem_start``.\n"
|
||||
"\n"
|
||||
" :param program: Program that performs the drawing operations.\n"
|
||||
" :type program: :class:`gpu.types.GPUShader`\n"
|
||||
" :param elem_start: First index to draw. When not provided or set to 0 drawing\n"
|
||||
" will start from the first element of the index buffer.\n"
|
||||
" :type elem_start: int\n"
|
||||
" :param elem_count: Number of elements of the index buffer to draw. When not\n"
|
||||
" provided or set to 0 all elements from ``elem_start`` to the end of the\n"
|
||||
" index buffer will be drawn.\n"
|
||||
" :type elem_count: int\n");
|
||||
static PyObject *pygpu_batch_draw_range(BPyGPUBatch *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
BPyGPUShader *py_program = nullptr;
|
||||
int elem_start = 0;
|
||||
int elem_count = 0;
|
||||
|
||||
static const char *_keywords[] = {"program", "elem_start", "elem_count", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O!" /* `program` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"i" /* `elem_start` */
|
||||
"i" /* `elem_count` */
|
||||
":GPUBatch.draw_range",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, &BPyGPUShader_Type, &py_program, &elem_start, &elem_count))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_batch_set_shader(self->batch, py_program->shader);
|
||||
GPU_batch_draw_range(self->batch, elem_start, elem_count);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_batch_program_use_begin(BPyGPUBatch *self)
|
||||
{
|
||||
if (!pygpu_batch_is_program_or_error(self)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_shader_bind(self->batch->shader);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_batch_program_use_end(BPyGPUBatch *self)
|
||||
{
|
||||
if (!pygpu_batch_is_program_or_error(self)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_shader_unbind();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_batch__tp_methods[] = {
|
||||
{"vertbuf_add",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_vertbuf_add),
|
||||
METH_O,
|
||||
pygpu_batch_vertbuf_add_doc},
|
||||
{"program_set",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_program_set),
|
||||
METH_O,
|
||||
pygpu_batch_program_set_doc},
|
||||
{"draw",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_draw),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_batch_draw_doc},
|
||||
{"draw_instanced",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_draw_instanced),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_batch_draw_instanced_doc},
|
||||
{"draw_range",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_draw_range),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_batch_draw_range_doc},
|
||||
{"_program_use_begin",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_program_use_begin),
|
||||
METH_NOARGS,
|
||||
""},
|
||||
{"_program_use_end",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_program_use_end),
|
||||
METH_NOARGS,
|
||||
""},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
|
||||
static int pygpu_batch__tp_traverse(BPyGPUBatch *self, visitproc visit, void *arg)
|
||||
{
|
||||
Py_VISIT(self->references);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pygpu_batch__tp_clear(BPyGPUBatch *self)
|
||||
{
|
||||
Py_CLEAR(self->references);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pygpu_batch__tp_is_gc(BPyGPUBatch *self)
|
||||
{
|
||||
return self->references != nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void pygpu_batch__tp_dealloc(BPyGPUBatch *self)
|
||||
{
|
||||
GPU_batch_discard(self->batch);
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
PyObject_GC_UnTrack(self);
|
||||
if (self->references) {
|
||||
pygpu_batch__tp_clear(self);
|
||||
Py_XDECREF(self->references);
|
||||
}
|
||||
#endif
|
||||
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch__tp_doc,
|
||||
".. class:: GPUBatch\n"
|
||||
"\n"
|
||||
" Reusable container for drawable geometry.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(type, buf, elem=None)\n"
|
||||
"\n"
|
||||
" :param type: The primitive type of geometry to be drawn.\n"
|
||||
" :type type: " PYDOC_PRIMTYPE_LITERAL
|
||||
"\n"
|
||||
" :param buf: Vertex buffer containing all or some of the attributes required for "
|
||||
"drawing.\n"
|
||||
" :type buf: :class:`gpu.types.GPUVertBuf`\n"
|
||||
" :param elem: An optional index buffer.\n"
|
||||
" :type elem: :class:`gpu.types.GPUIndexBuf` | None\n");
|
||||
PyTypeObject BPyGPUBatch_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUBatch",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUBatch),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_batch__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
#else
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
#endif
|
||||
/*tp_doc*/ pygpu_batch__tp_doc,
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/*tp_traverse*/ reinterpret_cast<traverseproc>(pygpu_batch__tp_traverse),
|
||||
#else
|
||||
/*tp_traverse*/ nullptr,
|
||||
#endif
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/*tp_clear*/ reinterpret_cast<inquiry>(pygpu_batch__tp_clear),
|
||||
#else
|
||||
/*tp_clear*/ nullptr,
|
||||
#endif
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_batch__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_batch__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/*tp_is_gc*/ reinterpret_cast<inquiry>(pygpu_batch__tp_is_gc),
|
||||
#else
|
||||
/*tp_is_gc*/ nullptr,
|
||||
#endif
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUBatch_CreatePyObject(gpu::Batch *batch)
|
||||
{
|
||||
BPyGPUBatch *self;
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
self = reinterpret_cast<BPyGPUBatch *>(_PyObject_GC_New(&BPyGPUBatch_Type));
|
||||
self->references = nullptr;
|
||||
#else
|
||||
self = PyObject_New(BPyGPUBatch, &BPyGPUBatch_Type);
|
||||
#endif
|
||||
|
||||
self->batch = batch;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef BPY_GPU_BATCH_CHECK_OBJ
|
||||
|
||||
} // namespace blender
|
||||
39
blender-5.2.0/source/blender/python/gpu/gpu_py_batch.hh
Normal file
39
blender-5.2.0/source/blender/python/gpu/gpu_py_batch.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
}
|
||||
|
||||
#define USE_GPU_PY_REFERENCES
|
||||
|
||||
extern PyTypeObject BPyGPUBatch_Type;
|
||||
|
||||
#define BPyGPUBatch_Check(v) (Py_TYPE(v) == &BPyGPUBatch_Type)
|
||||
|
||||
struct BPyGPUBatch {
|
||||
PyObject_HEAD
|
||||
/* The batch is owned, we may support thin wrapped batches later. */
|
||||
gpu::Batch *batch;
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Just to keep a user to prevent freeing buffers we're using. */
|
||||
PyObject *references;
|
||||
#endif
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUBatch_CreatePyObject(gpu::Batch *batch) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
796
blender-5.2.0/source/blender/python/gpu/gpu_py_buffer.cc
Normal file
796
blender-5.2.0/source/blender/python/gpu/gpu_py_buffer.cc
Normal file
@@ -0,0 +1,796 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the gpu.types.Buffer API.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
|
||||
#include "gpu_py_buffer.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define PYGPU_BUFFER_PROTOCOL
|
||||
#define MAX_DIMENSIONS 64
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility Functions
|
||||
* \{ */
|
||||
|
||||
static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len)
|
||||
{
|
||||
Py_ssize_t tot = shape[0];
|
||||
for (int i = 1; i < shape_len; i++) {
|
||||
tot *= shape[i];
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a,
|
||||
const Py_ssize_t shape_a_len,
|
||||
const Py_ssize_t *shape_b,
|
||||
const Py_ssize_t shape_b_len)
|
||||
{
|
||||
if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) !=
|
||||
pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len))
|
||||
{
|
||||
PyErr_SetString(PyExc_BufferError, "array size does not match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
|
||||
Py_ssize_t r_shape[MAX_DIMENSIONS],
|
||||
Py_ssize_t *r_shape_len)
|
||||
{
|
||||
Py_ssize_t shape_len = 0;
|
||||
if (PyLong_Check(shape_obj)) {
|
||||
shape_len = 1;
|
||||
if ((r_shape[0] = PyLong_AsSsize_t(shape_obj)) < 1) {
|
||||
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (PySequence_Check(shape_obj)) {
|
||||
shape_len = PySequence_Size(shape_obj);
|
||||
if (shape_len > MAX_DIMENSIONS) {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
|
||||
return false;
|
||||
}
|
||||
if (shape_len < 1) {
|
||||
PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < shape_len; i++) {
|
||||
PyObject *ob = PySequence_GetItem(shape_obj, i);
|
||||
if (!PyLong_Check(ob)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"invalid dimension %i, expected an int, not a %.200s",
|
||||
i,
|
||||
Py_TYPE(ob)->tp_name);
|
||||
Py_DECREF(ob);
|
||||
return false;
|
||||
}
|
||||
|
||||
r_shape[i] = PyLong_AsSsize_t(ob);
|
||||
Py_DECREF(ob);
|
||||
|
||||
if (r_shape[i] < 1) {
|
||||
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"invalid second argument expected a sequence "
|
||||
"or an int, not a %.200s",
|
||||
Py_TYPE(shape_obj)->tp_name);
|
||||
}
|
||||
|
||||
*r_shape_len = shape_len;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format)
|
||||
{
|
||||
switch (data_format) {
|
||||
case GPU_DATA_FLOAT:
|
||||
return "f";
|
||||
case GPU_DATA_INT:
|
||||
return "i";
|
||||
case GPU_DATA_UINT:
|
||||
return "I";
|
||||
case GPU_DATA_UBYTE:
|
||||
return "B";
|
||||
case GPU_DATA_UINT_24_8_DEPRECATED:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return "I";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BPyGPUBuffer API
|
||||
* \{ */
|
||||
|
||||
static BPyGPUBuffer *pygpu_buffer_make_from_data(PyObject *parent,
|
||||
const eGPUDataFormat format,
|
||||
const int shape_len,
|
||||
const Py_ssize_t *shape,
|
||||
void *buf)
|
||||
{
|
||||
BPyGPUBuffer *buffer = reinterpret_cast<BPyGPUBuffer *>(_PyObject_GC_New(&BPyGPU_BufferType));
|
||||
|
||||
buffer->parent = nullptr;
|
||||
buffer->format = format;
|
||||
buffer->shape_len = shape_len;
|
||||
buffer->shape = MEM_new_array_uninitialized<Py_ssize_t>(size_t(shape_len), "BPyGPUBuffer shape");
|
||||
memcpy(buffer->shape, shape, sizeof(*buffer->shape) * size_t(shape_len));
|
||||
buffer->buf.as_void = buf;
|
||||
|
||||
if (parent) {
|
||||
Py_INCREF(parent);
|
||||
buffer->parent = parent;
|
||||
BLI_assert(!PyObject_GC_IsTracked((PyObject *)buffer));
|
||||
PyObject_GC_Track(buffer);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer__sq_item(BPyGPUBuffer *self, Py_ssize_t i)
|
||||
{
|
||||
if (i >= self->shape[0] || i < 0) {
|
||||
PyErr_SetString(PyExc_IndexError, "array index out of range");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *formatstr = pygpu_buffer_formatstr(eGPUDataFormat(self->format));
|
||||
|
||||
if (self->shape_len == 1) {
|
||||
switch (self->format) {
|
||||
case GPU_DATA_FLOAT:
|
||||
return Py_BuildValue(formatstr, self->buf.as_float[i]);
|
||||
case GPU_DATA_INT:
|
||||
return Py_BuildValue(formatstr, self->buf.as_int[i]);
|
||||
case GPU_DATA_UBYTE:
|
||||
return Py_BuildValue(formatstr, self->buf.as_byte[i]);
|
||||
case GPU_DATA_UINT:
|
||||
case GPU_DATA_UINT_24_8_DEPRECATED:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return Py_BuildValue(formatstr, self->buf.as_uint[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int offset = i * GPU_texture_dataformat_size(eGPUDataFormat(self->format));
|
||||
for (int j = 1; j < self->shape_len; j++) {
|
||||
offset *= self->shape[j];
|
||||
}
|
||||
|
||||
return reinterpret_cast<PyObject *>(
|
||||
pygpu_buffer_make_from_data(reinterpret_cast<PyObject *>(self),
|
||||
eGPUDataFormat(self->format),
|
||||
self->shape_len - 1,
|
||||
self->shape + 1,
|
||||
self->buf.as_byte + offset));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer_to_list(BPyGPUBuffer *self)
|
||||
{
|
||||
const Py_ssize_t len = self->shape[0];
|
||||
PyObject *list = PyList_New(len);
|
||||
|
||||
for (Py_ssize_t i = 0; i < len; i++) {
|
||||
PyList_SET_ITEM(list, i, pygpu_buffer__sq_item(self, i));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_buffer_to_list_doc,
|
||||
".. method:: to_list()\n"
|
||||
"\n"
|
||||
" Return the buffer as a list.\n"
|
||||
"\n"
|
||||
" :return: The buffer as a list.\n"
|
||||
" :rtype: list\n");
|
||||
static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self)
|
||||
{
|
||||
PyObject *list;
|
||||
|
||||
if (self->shape_len > 1) {
|
||||
int i, len = self->shape[0];
|
||||
list = PyList_New(len);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* "BPyGPUBuffer *sub_tmp" is a temporary object created just to be read for nested lists.
|
||||
* That is why it is decremented/freed soon after.
|
||||
* TODO: For efficiency, avoid creating #BPyGPUBuffer when creating nested lists. */
|
||||
BPyGPUBuffer *sub_tmp = reinterpret_cast<BPyGPUBuffer *>(pygpu_buffer__sq_item(self, i));
|
||||
PyList_SET_ITEM(list, i, pygpu_buffer_to_list_recursive(sub_tmp));
|
||||
Py_DECREF(sub_tmp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
list = pygpu_buffer_to_list(self);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_buffer_dimensions_doc,
|
||||
"The size of the buffer for each dimension.\n"
|
||||
"\n"
|
||||
"Setting the dimensions is supported when the total number of elements is unchanged.\n"
|
||||
"\n"
|
||||
":type: list[int]\n");
|
||||
static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void * /*arg*/)
|
||||
{
|
||||
PyObject *list = PyList_New(self->shape_len);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < self->shape_len; i++) {
|
||||
PyList_SET_ITEM(list, i, PyLong_FromLong(self->shape[i]));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void * /*type*/)
|
||||
{
|
||||
Py_ssize_t shape[MAX_DIMENSIONS];
|
||||
Py_ssize_t shape_len = 0;
|
||||
|
||||
if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (shape_len != self->shape_len) {
|
||||
MEM_delete(self->shape);
|
||||
self->shape = MEM_new_array_uninitialized<Py_ssize_t>(size_t(shape_len), __func__);
|
||||
}
|
||||
|
||||
self->shape_len = shape_len;
|
||||
memcpy(self->shape, shape, sizeof(*self->shape) * size_t(shape_len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg)
|
||||
{
|
||||
Py_VISIT(self->parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__tp_clear(BPyGPUBuffer *self)
|
||||
{
|
||||
if (self->parent) {
|
||||
Py_CLEAR(self->parent);
|
||||
self->buf.as_void = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pygpu_buffer__tp_dealloc(BPyGPUBuffer *self)
|
||||
{
|
||||
if (self->parent) {
|
||||
PyObject_GC_UnTrack(self);
|
||||
Py_CLEAR(self->parent);
|
||||
}
|
||||
else if (self->buf.as_void) {
|
||||
MEM_delete_void(self->buf.as_void);
|
||||
}
|
||||
|
||||
MEM_delete(self->shape);
|
||||
|
||||
PyObject_GC_Del(self);
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer__tp_repr(BPyGPUBuffer *self)
|
||||
{
|
||||
PyObject *repr;
|
||||
|
||||
PyObject *list = pygpu_buffer_to_list_recursive(self);
|
||||
const char *typestr = PyC_StringEnum_FindIDFromValue(bpygpu_dataformat_items, self->format);
|
||||
|
||||
repr = PyUnicode_FromFormat("Buffer(%s, %R)", typestr, list);
|
||||
Py_DECREF(list);
|
||||
|
||||
return repr;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__sq_ass_item(BPyGPUBuffer *self, Py_ssize_t i, PyObject *v);
|
||||
|
||||
static int pygpu_buffer_ass_slice(BPyGPUBuffer *self,
|
||||
Py_ssize_t begin,
|
||||
Py_ssize_t end,
|
||||
PyObject *seq)
|
||||
{
|
||||
PyObject *item;
|
||||
int count, err = 0;
|
||||
|
||||
begin = std::max<Py_ssize_t>(begin, 0);
|
||||
end = std::min(end, self->shape[0]);
|
||||
begin = std::min(begin, end);
|
||||
|
||||
if (!PySequence_Check(seq)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"buffer[:] = value, invalid assignment. "
|
||||
"Expected a sequence, not an %.200s type",
|
||||
Py_TYPE(seq)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* re-use count var */
|
||||
if ((count = PySequence_Size(seq)) != (end - begin)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"buffer[:] = value, size mismatch in assignment. "
|
||||
"Expected: %d (given: %d)",
|
||||
count,
|
||||
end - begin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (count = begin; count < end; count++) {
|
||||
item = PySequence_GetItem(seq, count - begin);
|
||||
if (item) {
|
||||
err = pygpu_buffer__sq_ass_item(self, count, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
else {
|
||||
err = -1;
|
||||
}
|
||||
if (err) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
PyObject *length_ob, *init = nullptr;
|
||||
BPyGPUBuffer *buffer = nullptr;
|
||||
Py_ssize_t shape[MAX_DIMENSIONS];
|
||||
|
||||
Py_ssize_t shape_len = 0;
|
||||
|
||||
if (kwds && PyDict_Size(kwds)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_DATA_FLOAT};
|
||||
if (!PyArg_ParseTuple(
|
||||
args, "O&O|O: Buffer", PyC_ParseStringEnum, &pygpu_dataformat, &length_ob, &init))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (pygpu_dataformat.value_found == GPU_DATA_UINT_24_8_DEPRECATED) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning, "`UINT_24_8` is deprecated, use `FLOAT` instead", 1);
|
||||
}
|
||||
|
||||
if (!pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (init && PyObject_CheckBuffer(init)) {
|
||||
Py_buffer pybuffer;
|
||||
|
||||
if (PyObject_GetBuffer(init, &pybuffer, PyBUF_ND | PyBUF_FORMAT) == -1) {
|
||||
/* PyObject_GetBuffer raise a PyExc_BufferError */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_ssize_t *pybuffer_shape = pybuffer.shape;
|
||||
Py_ssize_t pybuffer_ndim = pybuffer.ndim;
|
||||
if (!pybuffer_shape) {
|
||||
pybuffer_shape = &pybuffer.len;
|
||||
pybuffer_ndim = 1;
|
||||
}
|
||||
|
||||
if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer_shape, pybuffer_ndim)) {
|
||||
buffer = pygpu_buffer_make_from_data(
|
||||
init, eGPUDataFormat(pygpu_dataformat.value_found), shape_len, shape, pybuffer.buf);
|
||||
}
|
||||
|
||||
PyBuffer_Release(&pybuffer);
|
||||
}
|
||||
else {
|
||||
buffer = BPyGPU_Buffer_CreatePyObject(pygpu_dataformat.value_found, shape, shape_len, nullptr);
|
||||
if (init && pygpu_buffer_ass_slice(buffer, 0, shape[0], init)) {
|
||||
Py_DECREF(buffer);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return reinterpret_cast<PyObject *>(buffer);
|
||||
}
|
||||
|
||||
static int pygpu_buffer__tp_is_gc(BPyGPUBuffer *self)
|
||||
{
|
||||
return self->parent != nullptr;
|
||||
}
|
||||
|
||||
/* BPyGPUBuffer sequence methods */
|
||||
|
||||
static Py_ssize_t pygpu_buffer__sq_length(BPyGPUBuffer *self)
|
||||
{
|
||||
return self->shape[0];
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer_slice(BPyGPUBuffer *self, Py_ssize_t begin, Py_ssize_t end)
|
||||
{
|
||||
PyObject *list;
|
||||
Py_ssize_t count;
|
||||
|
||||
begin = std::max<Py_ssize_t>(begin, 0);
|
||||
end = std::min(end, self->shape[0]);
|
||||
begin = std::min(begin, end);
|
||||
|
||||
list = PyList_New(end - begin);
|
||||
|
||||
for (count = begin; count < end; count++) {
|
||||
PyList_SET_ITEM(list, count - begin, pygpu_buffer__sq_item(self, count));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__sq_ass_item(BPyGPUBuffer *self, Py_ssize_t i, PyObject *v)
|
||||
{
|
||||
if (i >= self->shape[0] || i < 0) {
|
||||
PyErr_SetString(PyExc_IndexError, "array assignment index out of range");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (self->shape_len != 1) {
|
||||
BPyGPUBuffer *row = reinterpret_cast<BPyGPUBuffer *>(pygpu_buffer__sq_item(self, i));
|
||||
|
||||
if (row) {
|
||||
const int ret = pygpu_buffer_ass_slice(row, 0, self->shape[1], v);
|
||||
Py_DECREF(row);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (self->format) {
|
||||
case GPU_DATA_FLOAT:
|
||||
return PyArg_Parse(v, "f:Expected floats", &self->buf.as_float[i]) ? 0 : -1;
|
||||
case GPU_DATA_INT:
|
||||
return PyArg_Parse(v, "i:Expected ints", &self->buf.as_int[i]) ? 0 : -1;
|
||||
case GPU_DATA_UBYTE:
|
||||
return PyArg_Parse(v, "b:Expected ints", &self->buf.as_byte[i]) ? 0 : -1;
|
||||
case GPU_DATA_UINT:
|
||||
case GPU_DATA_UINT_24_8_DEPRECATED:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return PyArg_Parse(v, "I:Expected unsigned ints", &self->buf.as_uint[i]) ? 0 : -1;
|
||||
default:
|
||||
return 0; /* should never happen */
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer__mp_subscript(BPyGPUBuffer *self, PyObject *item)
|
||||
{
|
||||
if (PyIndex_Check(item)) {
|
||||
Py_ssize_t i;
|
||||
i = PyNumber_AsSsize_t(item, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (i < 0) {
|
||||
i += self->shape[0];
|
||||
}
|
||||
return pygpu_buffer__sq_item(self, i);
|
||||
}
|
||||
if (PySlice_Check(item)) {
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
|
||||
if (PySlice_GetIndicesEx(item, self->shape[0], &start, &stop, &step, &slicelength) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (slicelength <= 0) {
|
||||
return PyTuple_New(0);
|
||||
}
|
||||
if (step == 1) {
|
||||
return pygpu_buffer_slice(self, start, stop);
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "buffer indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__mp_ass_subscript(BPyGPUBuffer *self, PyObject *item, PyObject *value)
|
||||
{
|
||||
if (PyIndex_Check(item)) {
|
||||
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
if (i < 0) {
|
||||
i += self->shape[0];
|
||||
}
|
||||
return pygpu_buffer__sq_ass_item(self, i, value);
|
||||
}
|
||||
if (PySlice_Check(item)) {
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
|
||||
if (PySlice_GetIndicesEx(item, self->shape[0], &start, &stop, &step, &slicelength) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (step == 1) {
|
||||
return pygpu_buffer_ass_slice(self, start, stop, value);
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "buffer indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_buffer__tp_methods[] = {
|
||||
{"to_list",
|
||||
reinterpret_cast<PyCFunction>(pygpu_buffer_to_list_recursive),
|
||||
METH_NOARGS,
|
||||
pygpu_buffer_to_list_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyGetSetDef pygpu_buffer_getseters[] = {
|
||||
{"dimensions",
|
||||
reinterpret_cast<getter>(pygpu_buffer_dimensions_get),
|
||||
reinterpret_cast<setter>(pygpu_buffer_dimensions_set),
|
||||
pygpu_buffer_dimensions_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static PySequenceMethods pygpu_buffer__tp_as_sequence = {
|
||||
/*sq_length*/ reinterpret_cast<lenfunc>(pygpu_buffer__sq_length),
|
||||
/*sq_concat*/ nullptr,
|
||||
/*sq_repeat*/ nullptr,
|
||||
/*sq_item*/ reinterpret_cast<ssizeargfunc>(pygpu_buffer__sq_item),
|
||||
/*was_sq_slice*/ nullptr, /* DEPRECATED. Handled by #pygpu_buffer__sq_item. */
|
||||
/*sq_ass_item*/ reinterpret_cast<ssizeobjargproc>(pygpu_buffer__sq_ass_item),
|
||||
/*was_sq_ass_slice*/ nullptr, /* DEPRECATED. Handled by #pygpu_buffer__sq_ass_item. */
|
||||
/*sq_contains*/ nullptr,
|
||||
/*sq_inplace_concat*/ nullptr,
|
||||
/*sq_inplace_repeat*/ nullptr,
|
||||
};
|
||||
|
||||
static PyMappingMethods pygpu_buffer__tp_as_mapping = {
|
||||
/*mp_length*/ reinterpret_cast<lenfunc>(pygpu_buffer__sq_length),
|
||||
/*mp_subscript*/ reinterpret_cast<binaryfunc>(pygpu_buffer__mp_subscript),
|
||||
/*mp_ass_subscript*/ reinterpret_cast<objobjargproc>(pygpu_buffer__mp_ass_subscript),
|
||||
};
|
||||
|
||||
#ifdef PYGPU_BUFFER_PROTOCOL
|
||||
static void pygpu_buffer_strides_calc(const eGPUDataFormat format,
|
||||
const int shape_len,
|
||||
const Py_ssize_t *shape,
|
||||
Py_ssize_t *r_strides)
|
||||
{
|
||||
Py_ssize_t stride = GPU_texture_dataformat_size(format);
|
||||
for (int i = shape_len; i-- > 0;) {
|
||||
r_strides[i] = stride;
|
||||
stride *= shape[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Here is the buffer interface function */
|
||||
static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int flags)
|
||||
{
|
||||
if (UNLIKELY(view == nullptr)) {
|
||||
PyErr_SetString(PyExc_ValueError, "null view in get-buffer is obsolete");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(view, 0, sizeof(*view));
|
||||
|
||||
view->obj = reinterpret_cast<PyObject *>(self);
|
||||
view->buf = self->buf.as_void;
|
||||
view->len = bpygpu_Buffer_size(self);
|
||||
view->readonly = 0;
|
||||
view->itemsize = GPU_texture_dataformat_size(eGPUDataFormat(self->format));
|
||||
if (flags & PyBUF_FORMAT) {
|
||||
view->format = const_cast<char *>(pygpu_buffer_formatstr(eGPUDataFormat(self->format)));
|
||||
}
|
||||
if (flags & PyBUF_ND) {
|
||||
view->ndim = self->shape_len;
|
||||
view->shape = self->shape;
|
||||
}
|
||||
if (flags & PyBUF_STRIDES) {
|
||||
view->strides = MEM_new_array_uninitialized<Py_ssize_t>(size_t(view->ndim),
|
||||
"BPyGPUBuffer strides");
|
||||
pygpu_buffer_strides_calc(
|
||||
eGPUDataFormat(self->format), view->ndim, view->shape, view->strides);
|
||||
}
|
||||
view->suboffsets = nullptr;
|
||||
view->internal = nullptr;
|
||||
|
||||
Py_INCREF(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pygpu_buffer__bf_releasebuffer(PyObject * /*exporter*/, Py_buffer *view)
|
||||
{
|
||||
MEM_SAFE_DELETE(view->strides);
|
||||
}
|
||||
|
||||
static PyBufferProcs pygpu_buffer__tp_as_buffer = {
|
||||
/*bf_getbuffer*/ reinterpret_cast<getbufferproc>(pygpu_buffer__bf_getbuffer),
|
||||
/*bf_releasebuffer*/ static_cast<releasebufferproc>(pygpu_buffer__bf_releasebuffer),
|
||||
};
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_buffer__tp_doc,
|
||||
".. class:: Buffer\n"
|
||||
"\n"
|
||||
" For Python access to GPU functions requiring a pointer.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(format, dimensions, data)\n"
|
||||
"\n"
|
||||
" :param format: Format type to interpret the buffer.\n"
|
||||
" ``UINT_24_8`` is deprecated, use ``FLOAT`` instead.\n"
|
||||
" :type format: " PYDOC_DATAFORMAT_LITERAL
|
||||
"\n"
|
||||
" :param dimensions: Array describing the dimensions.\n"
|
||||
" :type dimensions: int | Sequence[int]\n"
|
||||
" :param data: Optional data array.\n"
|
||||
" :type data: Buffer | Sequence[float] | Sequence[int]\n");
|
||||
PyTypeObject BPyGPU_BufferType = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "Buffer",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUBuffer),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_buffer__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_compare*/ nullptr,
|
||||
/*tp_repr*/ reinterpret_cast<reprfunc>(pygpu_buffer__tp_repr),
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ &pygpu_buffer__tp_as_sequence,
|
||||
/*tp_as_mapping*/ &pygpu_buffer__tp_as_mapping,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
#ifdef PYGPU_BUFFER_PROTOCOL
|
||||
/*tp_as_buffer*/ &pygpu_buffer__tp_as_buffer,
|
||||
#else
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
#endif
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
/*tp_doc*/ pygpu_buffer__tp_doc,
|
||||
/*tp_traverse*/ reinterpret_cast<traverseproc>(pygpu_buffer__tp_traverse),
|
||||
/*tp_clear*/ reinterpret_cast<inquiry>(pygpu_buffer__tp_clear),
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_buffer__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_buffer_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_buffer__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ reinterpret_cast<inquiry>(pygpu_buffer__tp_is_gc),
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
static size_t pygpu_buffer_calc_size(const int format,
|
||||
const int shape_len,
|
||||
const Py_ssize_t *shape)
|
||||
{
|
||||
return pygpu_buffer_dimensions_tot_elem(shape, shape_len) *
|
||||
GPU_texture_dataformat_size(eGPUDataFormat(format));
|
||||
}
|
||||
|
||||
size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer)
|
||||
{
|
||||
return pygpu_buffer_calc_size(buffer->format, buffer->shape_len, buffer->shape);
|
||||
}
|
||||
|
||||
BPyGPUBuffer *BPyGPU_Buffer_CreatePyObject(const int format,
|
||||
const Py_ssize_t *shape,
|
||||
const int shape_len,
|
||||
void *buffer)
|
||||
{
|
||||
if (buffer == nullptr) {
|
||||
size_t size = pygpu_buffer_calc_size(format, shape_len, shape);
|
||||
buffer = MEM_new_zeroed(size, "BPyGPUBuffer buffer");
|
||||
}
|
||||
|
||||
return pygpu_buffer_make_from_data(nullptr, eGPUDataFormat(format), shape_len, shape, buffer);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
57
blender-5.2.0/source/blender/python/gpu/gpu_py_buffer.hh
Normal file
57
blender-5.2.0/source/blender/python/gpu/gpu_py_buffer.hh
Normal file
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern PyTypeObject BPyGPU_BufferType;
|
||||
|
||||
#define BPyGPU_Buffer_Check(v) (Py_TYPE(v) == &BPyGPU_BufferType)
|
||||
|
||||
/**
|
||||
* Buffer Object
|
||||
*
|
||||
* For Python access to GPU functions requiring a pointer.
|
||||
*/
|
||||
struct BPyGPUBuffer {
|
||||
PyObject_HEAD
|
||||
PyObject *parent;
|
||||
|
||||
int format;
|
||||
int shape_len;
|
||||
Py_ssize_t *shape;
|
||||
|
||||
union {
|
||||
char *as_byte;
|
||||
int *as_int;
|
||||
uint *as_uint;
|
||||
float *as_float;
|
||||
|
||||
void *as_void;
|
||||
} buf;
|
||||
};
|
||||
|
||||
[[nodiscard]] size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer);
|
||||
/**
|
||||
* Create a buffer object
|
||||
*
|
||||
* \param shape: An array of `shape_len` integers representing the size of each dimension.
|
||||
* \param buffer: When not NULL holds a contiguous buffer
|
||||
* with the correct format from which the buffer will be initialized
|
||||
*/
|
||||
[[nodiscard]] BPyGPUBuffer *BPyGPU_Buffer_CreatePyObject(int format,
|
||||
const Py_ssize_t *shape,
|
||||
int shape_len,
|
||||
void *buffer);
|
||||
|
||||
} // namespace blender
|
||||
518
blender-5.2.0/source/blender/python/gpu/gpu_py_capabilities.cc
Normal file
518
blender-5.2.0/source/blender/python/gpu/gpu_py_capabilities.cc
Normal file
@@ -0,0 +1,518 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_capabilities.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Functions
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_texture_size_get_doc,
|
||||
".. function:: max_texture_size_get()\n"
|
||||
"\n"
|
||||
" Get estimated maximum texture size to be able to handle.\n"
|
||||
"\n"
|
||||
" :return: Texture size.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_texture_size_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_texture_size());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_texture_layers_get_doc,
|
||||
".. function:: max_texture_layers_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of layers in texture.\n"
|
||||
"\n"
|
||||
" :return: Number of layers.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_texture_layers_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_texture_layers());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_textures_get_doc,
|
||||
".. function:: max_textures_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported texture image units used for\n"
|
||||
" accessing texture maps from the vertex shader and the\n"
|
||||
" fragment processor.\n"
|
||||
"\n"
|
||||
" :return: Texture image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_textures_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_textures());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_textures_vert_get_doc,
|
||||
".. function:: max_textures_vert_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported texture image units used for\n"
|
||||
" accessing texture maps from the vertex shader.\n"
|
||||
"\n"
|
||||
" :return: Texture image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_textures_vert_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_textures());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_textures_geom_get_doc,
|
||||
".. function:: max_textures_geom_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported texture image units used for\n"
|
||||
" accessing texture maps from the geometry shader.\n"
|
||||
"\n"
|
||||
" :return: Texture image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_textures_geom_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_textures());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_textures_frag_get_doc,
|
||||
".. function:: max_textures_frag_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported texture image units used for\n"
|
||||
" accessing texture maps from the fragment shader.\n"
|
||||
"\n"
|
||||
" :return: Texture image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_textures_frag_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_textures());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_images_get_doc,
|
||||
".. function:: max_images_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported number of image units.\n"
|
||||
"\n"
|
||||
" :return: Number of image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_images_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_images());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_uniforms_vert_get_doc,
|
||||
".. function:: max_uniforms_vert_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of values held in uniform variable\n"
|
||||
" storage for a vertex shader.\n"
|
||||
"\n"
|
||||
" :return: Number of values.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_uniforms_vert_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_uniforms_vert());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_uniforms_frag_get_doc,
|
||||
".. function:: max_uniforms_frag_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of values held in uniform variable\n"
|
||||
" storage for a fragment shader.\n"
|
||||
"\n"
|
||||
" :return: Number of values.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_uniforms_frag_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_uniforms_frag());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_batch_indices_get_doc,
|
||||
".. function:: max_batch_indices_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of vertex array indices.\n"
|
||||
"\n"
|
||||
" :return: Number of indices.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_batch_indices_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_batch_indices());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_batch_vertices_get_doc,
|
||||
".. function:: max_batch_vertices_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of vertex array vertices.\n"
|
||||
"\n"
|
||||
" :return: Number of vertices.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_batch_vertices_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_batch_vertices());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_vertex_attribs_get_doc,
|
||||
".. function:: max_vertex_attribs_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of vertex attributes accessible to\n"
|
||||
" a vertex shader.\n"
|
||||
"\n"
|
||||
" :return: Number of attributes.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_vertex_attribs_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_vertex_attribs());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_varying_floats_get_doc,
|
||||
".. function:: max_varying_floats_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of varying variables used by\n"
|
||||
" vertex and fragment shaders.\n"
|
||||
"\n"
|
||||
" :return: Number of variables.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_varying_floats_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_varying_floats());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_extensions_get_doc,
|
||||
".. function:: extensions_get()\n"
|
||||
"\n"
|
||||
" Get supported extensions in the current context.\n"
|
||||
"\n"
|
||||
" :return: Extensions.\n"
|
||||
" :rtype: tuple[str, ...]\n");
|
||||
static PyObject *pygpu_extensions_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int extensions_len = GPU_extensions_len();
|
||||
PyObject *ret = PyTuple_New(extensions_len);
|
||||
PyObject **ob_items = (reinterpret_cast<PyTupleObject *>(ret))->ob_item;
|
||||
for (int i = 0; i < extensions_len; i++) {
|
||||
ob_items[i] = PyUnicode_FromString(GPU_extension_get(i));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_compute_shader_support_get_doc,
|
||||
".. function:: compute_shader_support_get()\n"
|
||||
"\n"
|
||||
" Are compute shaders supported.\n"
|
||||
"\n"
|
||||
" :return: True when supported, False when not supported.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *pygpu_compute_shader_support_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
static bool deprecation_warning_issued = false;
|
||||
if (!deprecation_warning_issued) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"compute_shader_support_get is deprecated. All platforms have support for "
|
||||
"compute shaders.",
|
||||
1);
|
||||
deprecation_warning_issued = true;
|
||||
}
|
||||
|
||||
return PyBool_FromLong(true);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_shader_image_load_store_support_get_doc,
|
||||
".. function:: shader_image_load_store_support_get()\n"
|
||||
"\n"
|
||||
" Is image load/store supported.\n"
|
||||
"\n"
|
||||
" :return: True when supported, False when not supported.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *pygpu_shader_image_load_store_support_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
static bool deprecation_warning_issued = false;
|
||||
if (!deprecation_warning_issued) {
|
||||
PyErr_WarnEx(
|
||||
PyExc_DeprecationWarning,
|
||||
"shader_image_load_store_support_get is deprecated. All platforms have support for "
|
||||
"image load store.",
|
||||
1);
|
||||
deprecation_warning_issued = true;
|
||||
}
|
||||
/* Now required to start Blender. */
|
||||
return PyBool_FromLong(true);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_hdr_support_get_doc,
|
||||
".. function:: hdr_support_get()\n"
|
||||
"\n"
|
||||
" Return whether GPU backend supports High Dynamic range for viewport.\n"
|
||||
"\n"
|
||||
" :return: HDR support available.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *pygpu_hdr_support_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyBool_FromLong(GPU_hdr_support());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_work_group_count_get_doc,
|
||||
".. function:: max_work_group_count_get(index)\n"
|
||||
"\n"
|
||||
" Get maximum number of work groups that may be dispatched to a compute shader.\n"
|
||||
"\n"
|
||||
" :param index: Index of the dimension.\n"
|
||||
" :type index: int\n"
|
||||
" :return: Maximum number of work groups for the queried dimension.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_work_group_count_get(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int index;
|
||||
if (!PyArg_ParseTuple(args, "i", &index)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int max_work_group_count = GPU_max_work_group_count(index);
|
||||
|
||||
return PyLong_FromLong(max_work_group_count);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_work_group_size_get_doc,
|
||||
".. function:: max_work_group_size_get(index)\n"
|
||||
"\n"
|
||||
" Get maximum size of a work group that may be dispatched to a compute shader.\n"
|
||||
"\n"
|
||||
" :param index: Index of the dimension.\n"
|
||||
" :type index: int\n"
|
||||
" :return: Maximum size of a work group for the queried dimension.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_work_group_size_get(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int index;
|
||||
if (!PyArg_ParseTuple(args, "i", &index)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int max_work_group_size = GPU_max_work_group_size(index);
|
||||
|
||||
return PyLong_FromLong(max_work_group_size);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_capabilities__tp_methods[] = {
|
||||
{"max_texture_size_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_texture_size_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_texture_size_get_doc},
|
||||
{"max_texture_layers_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_texture_layers_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_texture_layers_get_doc},
|
||||
{"max_textures_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_textures_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_textures_get_doc},
|
||||
{"max_textures_vert_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_textures_vert_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_textures_vert_get_doc},
|
||||
{"max_textures_geom_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_textures_geom_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_textures_geom_get_doc},
|
||||
{"max_textures_frag_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_textures_frag_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_textures_frag_get_doc},
|
||||
{"max_images_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_images_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_images_get_doc},
|
||||
{"max_uniforms_vert_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_uniforms_vert_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_uniforms_vert_get_doc},
|
||||
{"max_uniforms_frag_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_uniforms_frag_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_uniforms_frag_get_doc},
|
||||
{"max_batch_indices_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_batch_indices_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_batch_indices_get_doc},
|
||||
{"max_batch_vertices_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_batch_vertices_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_batch_vertices_get_doc},
|
||||
{"max_vertex_attribs_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_vertex_attribs_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_vertex_attribs_get_doc},
|
||||
{"max_varying_floats_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_varying_floats_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_varying_floats_get_doc},
|
||||
{"extensions_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_extensions_get),
|
||||
METH_NOARGS,
|
||||
pygpu_extensions_get_doc},
|
||||
|
||||
{"compute_shader_support_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_compute_shader_support_get),
|
||||
METH_NOARGS,
|
||||
pygpu_compute_shader_support_get_doc},
|
||||
{"shader_image_load_store_support_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_shader_image_load_store_support_get),
|
||||
METH_NOARGS,
|
||||
pygpu_shader_image_load_store_support_get_doc},
|
||||
{"hdr_support_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_hdr_support_get),
|
||||
METH_NOARGS,
|
||||
pygpu_hdr_support_get_doc},
|
||||
{
|
||||
"max_work_group_count_get",
|
||||
static_cast<PyCFunction>(pygpu_max_work_group_count_get),
|
||||
METH_VARARGS,
|
||||
pygpu_max_work_group_count_get_doc,
|
||||
},
|
||||
{
|
||||
"max_work_group_size_get",
|
||||
static_cast<PyCFunction>(pygpu_max_work_group_size_get),
|
||||
METH_VARARGS,
|
||||
pygpu_max_work_group_size_get_doc,
|
||||
},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_capabilities__tp_doc,
|
||||
"This module provides access to the GPU capabilities.");
|
||||
static PyModuleDef pygpu_capabilities_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.capabilities",
|
||||
/*m_doc*/ pygpu_capabilities__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_capabilities__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_capabilities_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_capabilities_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_capabilities_init();
|
||||
|
||||
} // namespace blender
|
||||
161
blender-5.2.0/source/blender/python/gpu/gpu_py_compute.cc
Normal file
161
blender-5.2.0/source/blender/python/gpu/gpu_py_compute.cc
Normal file
@@ -0,0 +1,161 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_compute.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_compute.hh" /* own include */
|
||||
#include "gpu_py_shader.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_compute_dispatch_doc,
|
||||
".. function:: dispatch(shader, groups_x_len, groups_y_len, groups_z_len)\n"
|
||||
"\n"
|
||||
" Dispatches GPU compute.\n"
|
||||
"\n"
|
||||
" :param shader: The shader that you want to dispatch.\n"
|
||||
" :type shader: :class:`gpu.types.GPUShader`\n"
|
||||
" :param groups_x_len: Int for group x length:\n"
|
||||
" :type groups_x_len: int\n"
|
||||
" :param groups_y_len: Int for group y length:\n"
|
||||
" :type groups_y_len: int\n"
|
||||
" :param groups_z_len: Int for group z length:\n"
|
||||
" :type groups_z_len: int\n");
|
||||
static PyObject *pygpu_compute_dispatch(PyObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
BPyGPUShader *py_shader;
|
||||
int groups_x_len;
|
||||
int groups_y_len;
|
||||
int groups_z_len;
|
||||
|
||||
static const char *_keywords[] = {
|
||||
"shader", "groups_x_len", "groups_y_len", "groups_z_len", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O" /* `shader` */
|
||||
"i" /* `groups_x_len` */
|
||||
"i" /* `groups_y_len` */
|
||||
"i" /* `groups_z_len` */
|
||||
":dispatch",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &py_shader, &groups_x_len, &groups_y_len, &groups_z_len))
|
||||
{
|
||||
if (!BPyGPUShader_Check(py_shader)) {
|
||||
PyErr_Format(PyExc_TypeError, "Expected a GPUShader, got %s", Py_TYPE(py_shader)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check that groups do not exceed #GPU_max_work_group_count(). */
|
||||
const int max_work_group_count_x = GPU_max_work_group_count(0);
|
||||
const int max_work_group_count_y = GPU_max_work_group_count(1);
|
||||
const int max_work_group_count_z = GPU_max_work_group_count(2);
|
||||
|
||||
/* Report back to the user both the requested and the maximum supported value. */
|
||||
if (groups_x_len > GPU_max_work_group_count(0)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"groups_x_len (%d) exceeds maximum supported value (max work group count: %d)",
|
||||
groups_x_len,
|
||||
max_work_group_count_x);
|
||||
return nullptr;
|
||||
}
|
||||
if (groups_y_len > GPU_max_work_group_count(1)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"groups_y_len (%d) exceeds maximum supported value (max work group count: %d)",
|
||||
groups_y_len,
|
||||
max_work_group_count_y);
|
||||
return nullptr;
|
||||
}
|
||||
if (groups_z_len > GPU_max_work_group_count(2)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"groups_z_len (%d) exceeds maximum supported value (max work group count: %d)",
|
||||
groups_z_len,
|
||||
max_work_group_count_z);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Shader *shader = py_shader->shader;
|
||||
GPU_compute_dispatch(shader, groups_x_len, groups_y_len, groups_z_len);
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_compute__tp_methods[] = {
|
||||
{"dispatch",
|
||||
reinterpret_cast<PyCFunction>(pygpu_compute_dispatch),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_compute_dispatch_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_compute__tp_doc,
|
||||
"This module provides access to the global GPU compute functions.");
|
||||
static PyModuleDef pygpu_compute_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.compute",
|
||||
/*m_doc*/ pygpu_compute__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_compute__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_compute_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_compute_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/gpu/gpu_py_compute.hh
Normal file
17
blender-5.2.0/source/blender/python/gpu/gpu_py_compute.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_compute_init();
|
||||
|
||||
} // namespace blender
|
||||
266
blender-5.2.0/source/blender/python/gpu/gpu_py_element.cc
Normal file
266
blender-5.2.0/source/blender/python/gpu/gpu_py_element.cc
Normal file
@@ -0,0 +1,266 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_element.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name IndexBuf Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_IndexBuf__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
const char *error_prefix = "IndexBuf.__new__";
|
||||
bool ok = true;
|
||||
|
||||
PyC_StringEnum prim_type = {bpygpu_primtype_items, GPU_PRIM_NONE};
|
||||
PyObject *seq;
|
||||
|
||||
uint verts_per_prim;
|
||||
uint index_len;
|
||||
GPUIndexBufBuilder builder;
|
||||
|
||||
static const char *_keywords[] = {"type", "seq", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"$" /* Keyword only arguments. */
|
||||
"O&" /* `type` */
|
||||
"O" /* `seq` */
|
||||
":IndexBuf.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, PyC_ParseStringEnum, &prim_type, &seq))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
verts_per_prim = GPU_indexbuf_primitive_len(GPUPrimType(prim_type.value_found));
|
||||
if (verts_per_prim == -1) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"The argument 'type' must be "
|
||||
"'POINTS', 'LINES', 'TRIS', 'LINES_ADJ' or 'TRIS_ADJ'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PyObject_CheckBuffer(seq)) {
|
||||
Py_buffer pybuffer;
|
||||
|
||||
if (PyObject_GetBuffer(seq, &pybuffer, PyBUF_FORMAT | PyBUF_ND) == -1) {
|
||||
/* PyObject_GetBuffer already handles error messages. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pybuffer.ndim != 1 && pybuffer.shape[1] != verts_per_prim) {
|
||||
PyErr_Format(PyExc_ValueError, "Each primitive must exactly %d indices", verts_per_prim);
|
||||
PyBuffer_Release(&pybuffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pybuffer.itemsize != 4 ||
|
||||
PyC_StructFmt_type_is_float_any(PyC_StructFmt_type_from_str(pybuffer.format)))
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Each index must be an 4-bytes integer value");
|
||||
PyBuffer_Release(&pybuffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
index_len = pybuffer.shape[0];
|
||||
if (pybuffer.ndim != 1) {
|
||||
index_len *= pybuffer.shape[1];
|
||||
}
|
||||
|
||||
/* The `vertex_len` parameter is only used for asserts in the Debug build. */
|
||||
/* Not very useful in python since scripts are often tested in Release build. */
|
||||
/* Use `INT_MAX` instead of the actual number of vertices. */
|
||||
GPU_indexbuf_init(&builder, GPUPrimType(prim_type.value_found), index_len, INT_MAX);
|
||||
|
||||
uint *buf = static_cast<uint *>(pybuffer.buf);
|
||||
for (uint i = index_len; i--; buf++) {
|
||||
GPU_indexbuf_add_generic_vert(&builder, *buf);
|
||||
}
|
||||
|
||||
PyBuffer_Release(&pybuffer);
|
||||
}
|
||||
else {
|
||||
PyObject *seq_fast = PySequence_Fast(seq, error_prefix);
|
||||
|
||||
if (seq_fast == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint seq_len = PySequence_Fast_GET_SIZE(seq_fast);
|
||||
|
||||
PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
|
||||
|
||||
index_len = seq_len * verts_per_prim;
|
||||
|
||||
/* The `vertex_len` parameter is only used for asserts in the Debug build. */
|
||||
/* Not very useful in python since scripts are often tested in Release build. */
|
||||
/* Use `INT_MAX` instead of the actual number of vertices. */
|
||||
GPU_indexbuf_init(&builder, GPUPrimType(prim_type.value_found), index_len, INT_MAX);
|
||||
|
||||
if (verts_per_prim == 1) {
|
||||
for (uint i = 0; i < seq_len; i++) {
|
||||
GPU_indexbuf_add_generic_vert(&builder, PyC_Long_AsU32(seq_items[i]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
int values[4];
|
||||
for (uint i = 0; i < seq_len; i++) {
|
||||
PyObject *seq_fast_item = PySequence_Fast(seq_items[i], error_prefix);
|
||||
if (seq_fast_item == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s: expected a sequence, got %s",
|
||||
error_prefix,
|
||||
Py_TYPE(seq_items[i])->tp_name);
|
||||
ok = false;
|
||||
goto finally;
|
||||
}
|
||||
|
||||
ok = PyC_AsArray_FAST(values,
|
||||
sizeof(*values),
|
||||
seq_fast_item,
|
||||
verts_per_prim,
|
||||
&PyLong_Type,
|
||||
error_prefix) == 0;
|
||||
|
||||
if (ok) {
|
||||
for (uint j = 0; j < verts_per_prim; j++) {
|
||||
GPU_indexbuf_add_generic_vert(&builder, values[j]);
|
||||
}
|
||||
}
|
||||
Py_DECREF(seq_fast_item);
|
||||
}
|
||||
}
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
finally:
|
||||
|
||||
Py_DECREF(seq_fast);
|
||||
}
|
||||
|
||||
if (ok == false) {
|
||||
MEM_delete(builder.data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return BPyGPUIndexBuf_CreatePyObject(GPU_indexbuf_build(&builder));
|
||||
}
|
||||
|
||||
static void pygpu_IndexBuf__tp_dealloc(BPyGPUIndexBuf *self)
|
||||
{
|
||||
GPU_indexbuf_discard(self->elem);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_IndexBuf__tp_doc,
|
||||
".. class:: GPUIndexBuf\n"
|
||||
"\n"
|
||||
" Contains an index buffer.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(type, seq)\n"
|
||||
"\n"
|
||||
" :param type: The primitive type this index buffer is composed of.\n"
|
||||
" :type type: Literal['POINTS', 'LINES', 'TRIS', 'LINES_ADJ', 'TRIS_ADJ']\n"
|
||||
" :param seq: Indices this index buffer will contain.\n"
|
||||
" Whether a 1D or 2D sequence is required depends on the type.\n"
|
||||
" Optionally the sequence can support the buffer protocol.\n"
|
||||
" :type seq: Buffer | Sequence[int] | Sequence[Sequence[int]]\n");
|
||||
PyTypeObject BPyGPUIndexBuf_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUIndexBuf",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUIndexBuf),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_IndexBuf__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_IndexBuf__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ nullptr,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_IndexBuf__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUIndexBuf_CreatePyObject(gpu::IndexBuf *elem)
|
||||
{
|
||||
BPyGPUIndexBuf *self;
|
||||
|
||||
self = PyObject_New(BPyGPUIndexBuf, &BPyGPUIndexBuf_Type);
|
||||
self->elem = elem;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
30
blender-5.2.0/source/blender/python/gpu/gpu_py_element.hh
Normal file
30
blender-5.2.0/source/blender/python/gpu/gpu_py_element.hh
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class IndexBuf;
|
||||
}
|
||||
|
||||
extern PyTypeObject BPyGPUIndexBuf_Type;
|
||||
|
||||
#define BPyGPUIndexBuf_Check(v) (Py_TYPE(v) == &BPyGPUIndexBuf_Type)
|
||||
|
||||
struct BPyGPUIndexBuf {
|
||||
PyObject_HEAD
|
||||
gpu::IndexBuf *elem;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUIndexBuf_CreatePyObject(gpu::IndexBuf *elem);
|
||||
|
||||
} // namespace blender
|
||||
958
blender-5.2.0/source/blender/python/gpu/gpu_py_framebuffer.cc
Normal file
958
blender-5.2.0/source/blender/python/gpu/gpu_py_framebuffer.cc
Normal file
@@ -0,0 +1,958 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the framebuffer 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 "GPU_context.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_init_exit.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_buffer.hh"
|
||||
#include "gpu_py_framebuffer.hh" /* own include */
|
||||
#include "gpu_py_texture.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name gpu::FrameBuffer Common Utilities
|
||||
* \{ */
|
||||
|
||||
static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
|
||||
{
|
||||
if (UNLIKELY(bpygpu_fb->fb == nullptr)) {
|
||||
PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PYGPU_FRAMEBUFFER_CHECK_OBJ(bpygpu) \
|
||||
{ \
|
||||
if (UNLIKELY(pygpu_framebuffer_valid_check(bpygpu) == -1)) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
static void pygpu_framebuffer_free_if_possible(gpu::FrameBuffer *fb)
|
||||
{
|
||||
if (GPU_is_init()) {
|
||||
GPU_framebuffer_free(fb);
|
||||
}
|
||||
else {
|
||||
printf("PyFramebuffer freed after the context has been destroyed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
if (self->fb) {
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
GPU_framebuffer_py_reference_set(self->fb, nullptr);
|
||||
if (!self->shared_reference)
|
||||
#endif
|
||||
{
|
||||
pygpu_framebuffer_free_if_possible(self->fb);
|
||||
}
|
||||
|
||||
self->fb = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */
|
||||
#define GPU_PY_FRAMEBUFFER_STACK_LEN 16
|
||||
|
||||
static bool pygpu_framebuffer_stack_push_and_bind_or_error(gpu::FrameBuffer *fb)
|
||||
{
|
||||
if (GPU_framebuffer_stack_level_get() >= GPU_PY_FRAMEBUFFER_STACK_LEN) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"Maximum framebuffer stack depth " STRINGIFY(GPU_PY_FRAMEBUFFER_STACK_LEN) " reached");
|
||||
return false;
|
||||
}
|
||||
GPU_framebuffer_push(GPU_framebuffer_active_get());
|
||||
GPU_framebuffer_bind(fb);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_framebuffer_stack_pop_and_restore_or_error(gpu::FrameBuffer *fb)
|
||||
{
|
||||
if (GPU_framebuffer_stack_level_get() == 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Minimum framebuffer stack depth reached");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fb && !GPU_framebuffer_bound(fb)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Framebuffer is not bound");
|
||||
return false;
|
||||
}
|
||||
|
||||
gpu::FrameBuffer *fb_prev = GPU_framebuffer_pop();
|
||||
GPU_framebuffer_bind(fb_prev);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Stack (Context Manager)
|
||||
*
|
||||
* Safer alternative to ensure balanced push/pop calls.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct PyFrameBufferStackContext {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
BPyGPUFrameBuffer *py_fb;
|
||||
int level;
|
||||
};
|
||||
|
||||
static void pygpu_framebuffer_stack_context__tp_dealloc(PyFrameBufferStackContext *self)
|
||||
{
|
||||
Py_DECREF(self->py_fb);
|
||||
PyObject_DEL(self);
|
||||
}
|
||||
|
||||
static PyObject *pygpu_framebuffer_stack_context_enter(PyFrameBufferStackContext *self)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self->py_fb);
|
||||
|
||||
/* sanity - should never happen */
|
||||
if (self->level != -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Already in use");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!pygpu_framebuffer_stack_push_and_bind_or_error(self->py_fb->fb)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
self->level = GPU_framebuffer_stack_level_get();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_framebuffer_stack_context_exit(PyFrameBufferStackContext *self,
|
||||
PyObject * /*args*/)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self->py_fb);
|
||||
|
||||
/* sanity - should never happen */
|
||||
if (self->level == -1) {
|
||||
fprintf(stderr, "Not yet in use\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int level = GPU_framebuffer_stack_level_get();
|
||||
if (level != self->level) {
|
||||
fprintf(stderr, "Level of bind mismatch, expected %d, got %d\n", self->level, level);
|
||||
}
|
||||
|
||||
if (!pygpu_framebuffer_stack_pop_and_restore_or_error(self->py_fb->fb)) {
|
||||
return nullptr;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_framebuffer_stack_context__tp_methods[] = {
|
||||
{"__enter__",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_stack_context_enter),
|
||||
METH_NOARGS},
|
||||
{"__exit__",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_stack_context_exit),
|
||||
METH_VARARGS},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyTypeObject FramebufferStackContext_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUFrameBufferStackContext",
|
||||
/*tp_basicsize*/ sizeof(PyFrameBufferStackContext),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_framebuffer_stack_context__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ nullptr,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_framebuffer_stack_context__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_bind_doc,
|
||||
".. method:: bind()\n"
|
||||
"\n"
|
||||
" Context manager to ensure balanced bind calls, even in the case of an error.\n");
|
||||
static PyObject *pygpu_framebuffer_bind(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
PyFrameBufferStackContext *ret = PyObject_New(PyFrameBufferStackContext,
|
||||
&FramebufferStackContext_Type);
|
||||
ret->py_fb = self;
|
||||
ret->level = -1;
|
||||
Py_INCREF(self);
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUFramebuffer Type
|
||||
* \{ */
|
||||
|
||||
/* Fill in the GPUAttachment according to the PyObject parameter.
|
||||
* PyObject *o can be nullptr, Py_None, BPyGPUTexture or a dictionary containing the keyword
|
||||
* "texture" and the optional keywords "layer" and "mip". Returns false on error. In this case, a
|
||||
* python message will be raised and GPUAttachment will not be touched. */
|
||||
static bool pygpu_framebuffer_new_parse_arg(PyObject *o, GPUAttachment *r_attach)
|
||||
{
|
||||
GPUAttachment tmp_attach = GPU_ATTACHMENT_NONE;
|
||||
|
||||
if (!o || o == Py_None) {
|
||||
/* Pass. */;
|
||||
}
|
||||
else if (BPyGPUTexture_Check(o)) {
|
||||
if (!bpygpu_ParseTexture(o, &tmp_attach.tex)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char *c_texture = "texture";
|
||||
const char *c_layer = "layer";
|
||||
const char *c_mip = "mip";
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
while (PyDict_Next(o, &pos, &key, &value)) {
|
||||
if (!PyUnicode_Check(key)) {
|
||||
PyErr_SetString(PyExc_TypeError, "keywords must be strings");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c_texture && PyUnicode_CompareWithASCIIString(key, c_texture) == 0) {
|
||||
/* Compare only once. */
|
||||
c_texture = nullptr;
|
||||
if (!bpygpu_ParseTexture(value, &tmp_attach.tex)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (c_layer && PyUnicode_CompareWithASCIIString(key, c_layer) == 0) {
|
||||
/* Compare only once. */
|
||||
c_layer = nullptr;
|
||||
tmp_attach.layer = PyLong_AsLong(value);
|
||||
if (tmp_attach.layer == -1 && PyErr_Occurred()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (c_mip && PyUnicode_CompareWithASCIIString(key, c_mip) == 0) {
|
||||
/* Compare only once. */
|
||||
c_mip = nullptr;
|
||||
tmp_attach.mip = PyLong_AsLong(value);
|
||||
if (tmp_attach.mip == -1 && PyErr_Occurred()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "'%U' is an invalid keyword argument for this attribute", key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*r_attach = tmp_attach;
|
||||
return true;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_framebuffer__tp_new(PyTypeObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!GPU_context_active_get()) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "No active GPU context found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject *depth_attachment = nullptr;
|
||||
PyObject *color_attachements = nullptr;
|
||||
static const char *_keywords[] = {"depth_slot", "color_slots", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O" /* `depth_slot` */
|
||||
"O" /* `color_slots` */
|
||||
":GPUFrameBuffer.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &depth_attachment, &color_attachements))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Keep in sync with #GPU_FB_MAX_COLOR_ATTACHMENT.
|
||||
* TODO: share the define. */
|
||||
#define BPYGPU_FB_MAX_COLOR_ATTACHMENT 6
|
||||
|
||||
GPUAttachment config[BPYGPU_FB_MAX_COLOR_ATTACHMENT + 1];
|
||||
|
||||
if (!pygpu_framebuffer_new_parse_arg(depth_attachment, &config[0])) {
|
||||
return nullptr;
|
||||
}
|
||||
if (config[0].tex && !GPU_texture_has_depth_format(config[0].tex)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Depth texture with incompatible format");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int color_attachements_len = 0;
|
||||
if (color_attachements && color_attachements != Py_None) {
|
||||
if (PySequence_Check(color_attachements)) {
|
||||
color_attachements_len = PySequence_Size(color_attachements);
|
||||
if (color_attachements_len > BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"too many attachments, max is " STRINGIFY(BPYGPU_FB_MAX_COLOR_ATTACHMENT));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < color_attachements_len; i++) {
|
||||
PyObject *o = PySequence_GetItem(color_attachements, i);
|
||||
bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i + 1]);
|
||||
Py_DECREF(o);
|
||||
if (!ok) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!pygpu_framebuffer_new_parse_arg(color_attachements, &config[1])) {
|
||||
return nullptr;
|
||||
}
|
||||
color_attachements_len = 1;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::FrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
|
||||
GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
|
||||
|
||||
return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_is_bound_doc,
|
||||
"Checks if this is the active frame-buffer in the context.\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
static PyObject *pygpu_framebuffer_is_bound(BPyGPUFrameBuffer *self, void * /*type*/)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
return PyBool_FromLong(GPU_framebuffer_bound(self->fb));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_clear_doc,
|
||||
".. method:: clear(*, color=None, depth=None, stencil=None)\n"
|
||||
"\n"
|
||||
" Fill color, depth and stencil textures with specific value.\n"
|
||||
" Common values: color=(0.0, 0.0, 0.0, 1.0), depth=1.0, stencil=0.\n"
|
||||
"\n"
|
||||
" :param color: Sequence of 3 or 4 floats representing ``(r, g, b, a)``.\n"
|
||||
" :type color: Sequence[float] | None\n"
|
||||
" :param depth: depth value.\n"
|
||||
" :type depth: float | None\n"
|
||||
" :param stencil: stencil value.\n"
|
||||
" :type stencil: int | None\n");
|
||||
static PyObject *pygpu_framebuffer_clear(BPyGPUFrameBuffer *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
|
||||
if (!GPU_framebuffer_bound(self->fb)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject *py_col = nullptr;
|
||||
PyObject *py_depth = nullptr;
|
||||
PyObject *py_stencil = nullptr;
|
||||
|
||||
static const char *_keywords[] = {"color", "depth", "stencil", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O" /* `color` */
|
||||
"O" /* `depth` */
|
||||
"O" /* `stencil` */
|
||||
":clear",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &py_col, &py_depth, &py_stencil)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPUFrameBufferBits buffers = GPUFrameBufferBits(0);
|
||||
float col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
float depth = 1.0f;
|
||||
uint stencil = 0;
|
||||
|
||||
if (py_col && py_col != Py_None) {
|
||||
if (mathutils_array_parse(
|
||||
col, 3, 4, py_col, "gpu::FrameBuffer.clear(), invalid 'color' arg") == -1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
buffers |= GPU_COLOR_BIT;
|
||||
}
|
||||
|
||||
if (py_depth && py_depth != Py_None) {
|
||||
depth = PyFloat_AsDouble(py_depth);
|
||||
if (PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
buffers |= GPU_DEPTH_BIT;
|
||||
}
|
||||
|
||||
if (py_stencil && py_stencil != Py_None) {
|
||||
if ((stencil = PyC_Long_AsU32(py_stencil)) == uint(-1)) {
|
||||
return nullptr;
|
||||
}
|
||||
buffers |= GPU_STENCIL_BIT;
|
||||
}
|
||||
|
||||
GPU_framebuffer_clear(self->fb, buffers, double4(UNPACK4(col)), depth, stencil);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_viewport_set_doc,
|
||||
".. method:: viewport_set(x, y, xsize, ysize)\n"
|
||||
"\n"
|
||||
" Set the viewport for this framebuffer object.\n"
|
||||
" Note: The viewport state is not saved upon framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x of the viewport rectangle, in pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y of the viewport rectangle, in pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the viewport.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the viewport.\n"
|
||||
" :type ysize: int\n");
|
||||
static PyObject *pygpu_framebuffer_viewport_set(BPyGPUFrameBuffer *self, PyObject *args)
|
||||
{
|
||||
int x, y, xsize, ysize;
|
||||
if (!PyArg_ParseTuple(args, "iiii:viewport_set", &x, &y, &xsize, &ysize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_framebuffer_viewport_set(self->fb, x, y, xsize, ysize);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_viewport_get_doc,
|
||||
".. method:: viewport_get()\n"
|
||||
"\n"
|
||||
" Returns position and dimension to current viewport.\n"
|
||||
"\n"
|
||||
" :return: The viewport as ``(x, y, width, height)``.\n"
|
||||
" :rtype: tuple[int, int, int, int]\n");
|
||||
static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
int viewport[4];
|
||||
GPU_framebuffer_viewport_get(self->fb, viewport);
|
||||
|
||||
PyObject *ret = PyTuple_New(4);
|
||||
PyTuple_SET_ITEMS(ret,
|
||||
PyLong_FromLong(viewport[0]),
|
||||
PyLong_FromLong(viewport[1]),
|
||||
PyLong_FromLong(viewport[2]),
|
||||
PyLong_FromLong(viewport[3]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_read_color_doc,
|
||||
".. method:: read_color(x, y, xsize, ysize, channels, slot, format, *, data=None)\n"
|
||||
"\n"
|
||||
" Read a block of pixels from the frame buffer.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x of a rectangular block of pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y of a rectangular block of pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the pixel rectangle.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the pixel rectangle.\n"
|
||||
" :type ysize: int\n"
|
||||
" :param channels: Number of components to read.\n"
|
||||
" :type channels: int\n"
|
||||
" :param slot: The framebuffer slot to read data from.\n"
|
||||
" :type slot: int\n"
|
||||
" :param format: The format that describes the content of a single channel.\n"
|
||||
" ``UINT_24_8`` is deprecated, use ``FLOAT`` instead.\n"
|
||||
" :type format: " PYDOC_DATAFORMAT_LITERAL
|
||||
"\n"
|
||||
" :param data: Optional Buffer object to fill with the pixels values.\n"
|
||||
" :type data: :class:`gpu.types.Buffer` | None\n"
|
||||
" :return: The Buffer with the read pixels.\n"
|
||||
" :rtype: :class:`gpu.types.Buffer`\n");
|
||||
static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
int x, y, w, h, channels;
|
||||
uint slot;
|
||||
PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items,
|
||||
int(gpu::TextureFormat::UNORM_8_8_8_8)};
|
||||
BPyGPUBuffer *py_buffer = nullptr;
|
||||
PyC_TypeOrNone py_buffer_or_none = PyC_TYPE_OR_NONE_INIT(&BPyGPU_BufferType, &py_buffer);
|
||||
|
||||
static const char *_keywords[] = {
|
||||
"x", "y", "xsize", "ysize", "channels", "slot", "format", "data", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"i" /* `x` */
|
||||
"i" /* `y` */
|
||||
"i" /* `xsize` */
|
||||
"i" /* `ysize` */
|
||||
"i" /* `channels` */
|
||||
"I" /* `slot` */
|
||||
"O&" /* `format` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `data` */
|
||||
":read_color",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kwds,
|
||||
&_parser,
|
||||
&x,
|
||||
&y,
|
||||
&w,
|
||||
&h,
|
||||
&channels,
|
||||
&slot,
|
||||
PyC_ParseStringEnum,
|
||||
&pygpu_dataformat,
|
||||
PyC_ParseTypeOrNone,
|
||||
&py_buffer_or_none))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pygpu_dataformat.value_found == GPU_DATA_UINT_24_8_DEPRECATED) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning, "`UINT_24_8` is deprecated, use `FLOAT` instead", 1);
|
||||
}
|
||||
|
||||
if (!IN_RANGE_INCL(channels, 1, 4)) {
|
||||
PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
|
||||
PyErr_SetString(PyExc_ValueError, "slot overflow");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int2 extent = GPU_framebuffer_extent_get(self->fb);
|
||||
if (x < 0 || w < 0 || x + w > extent.x || y < 0 || h < 0 || y + h > extent.y) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Trying to read color outside the extent of the framebuffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (py_buffer) {
|
||||
if (pygpu_dataformat.value_found != py_buffer->format) {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"the format of the buffer is different from that specified");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t size_curr = bpygpu_Buffer_size(py_buffer);
|
||||
size_t size_expected = w * h * channels *
|
||||
GPU_texture_dataformat_size(
|
||||
eGPUDataFormat(pygpu_dataformat.value_found));
|
||||
if (size_curr < size_expected) {
|
||||
PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
|
||||
return nullptr;
|
||||
}
|
||||
Py_INCREF(py_buffer);
|
||||
}
|
||||
else {
|
||||
const Py_ssize_t shape[3] = {h, w, channels};
|
||||
py_buffer = BPyGPU_Buffer_CreatePyObject(pygpu_dataformat.value_found, shape, 3, nullptr);
|
||||
BLI_assert(bpygpu_Buffer_size(py_buffer) ==
|
||||
size_t(w) * size_t(h) * size_t(channels) *
|
||||
GPU_texture_dataformat_size(eGPUDataFormat(pygpu_dataformat.value_found)));
|
||||
}
|
||||
|
||||
GPU_framebuffer_read_color(self->fb,
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
channels,
|
||||
int(slot),
|
||||
eGPUDataFormat(pygpu_dataformat.value_found),
|
||||
py_buffer->buf.as_void);
|
||||
|
||||
return reinterpret_cast<PyObject *>(py_buffer);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_read_depth_doc,
|
||||
".. method:: read_depth(x, y, xsize, ysize, *, data=None)\n"
|
||||
"\n"
|
||||
" Read a pixel depth block from the frame buffer.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x of a rectangular block of pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y of a rectangular block of pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the pixel rectangle.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the pixel rectangle.\n"
|
||||
" :type ysize: int\n"
|
||||
" :param data: Optional Buffer object to fill with the pixels values.\n"
|
||||
" :type data: :class:`gpu.types.Buffer` | None\n"
|
||||
" :return: The Buffer with the read pixels.\n"
|
||||
" :rtype: :class:`gpu.types.Buffer`\n");
|
||||
static PyObject *pygpu_framebuffer_read_depth(BPyGPUFrameBuffer *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
int x, y, w, h;
|
||||
BPyGPUBuffer *py_buffer = nullptr;
|
||||
PyC_TypeOrNone py_buffer_or_none = PyC_TYPE_OR_NONE_INIT(&BPyGPU_BufferType, &py_buffer);
|
||||
|
||||
static const char *_keywords[] = {"x", "y", "xsize", "ysize", "data", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"i" /* `x` */
|
||||
"i" /* `y` */
|
||||
"i" /* `xsize` */
|
||||
"i" /* `ysize` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `data` */
|
||||
":read_depth",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &x, &y, &w, &h, PyC_ParseTypeOrNone, &py_buffer_or_none))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int2 extent = GPU_framebuffer_extent_get(self->fb);
|
||||
if (x < 0 || w < 0 || x + w > extent.x || y < 0 || h < 0 || y + h > extent.y) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Trying to read depth outside the extent of the framebuffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (py_buffer) {
|
||||
if (py_buffer->format != GPU_DATA_FLOAT) {
|
||||
PyErr_SetString(PyExc_AttributeError, "the format of the buffer must be 'GPU_DATA_FLOAT'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t size_curr = bpygpu_Buffer_size(py_buffer);
|
||||
size_t size_expected = w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT);
|
||||
if (size_curr < size_expected) {
|
||||
PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
|
||||
return nullptr;
|
||||
}
|
||||
Py_INCREF(py_buffer);
|
||||
}
|
||||
else {
|
||||
const Py_ssize_t shape[2] = {h, w};
|
||||
py_buffer = BPyGPU_Buffer_CreatePyObject(GPU_DATA_FLOAT, shape, 2, nullptr);
|
||||
BLI_assert(bpygpu_Buffer_size(py_buffer) ==
|
||||
w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT));
|
||||
}
|
||||
|
||||
GPU_framebuffer_read_depth(self->fb, x, y, w, h, GPU_DATA_FLOAT, py_buffer->buf.as_void);
|
||||
|
||||
return reinterpret_cast<PyObject *>(py_buffer);
|
||||
}
|
||||
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_free_doc,
|
||||
".. method:: free()\n"
|
||||
"\n"
|
||||
" Free the framebuffer object.\n"
|
||||
" The framebuffer will no longer be accessible.\n");
|
||||
static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
pygpu_framebuffer_free_safe(self);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
pygpu_framebuffer_free_safe(self);
|
||||
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject *>(self));
|
||||
}
|
||||
|
||||
static PyGetSetDef pygpu_framebuffer__tp_getseters[] = {
|
||||
{"is_bound",
|
||||
reinterpret_cast<getter>(pygpu_framebuffer_is_bound),
|
||||
static_cast<setter>(nullptr),
|
||||
pygpu_framebuffer_is_bound_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_framebuffer__tp_methods[] = {
|
||||
{"bind",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_bind),
|
||||
METH_NOARGS,
|
||||
pygpu_framebuffer_bind_doc},
|
||||
{"clear",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_clear),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_framebuffer_clear_doc},
|
||||
{"viewport_set",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_viewport_set),
|
||||
METH_VARARGS,
|
||||
pygpu_framebuffer_viewport_set_doc},
|
||||
{"viewport_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_viewport_get),
|
||||
METH_NOARGS,
|
||||
pygpu_framebuffer_viewport_get_doc},
|
||||
{"read_color",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_read_color),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_framebuffer_read_color_doc},
|
||||
{"read_depth",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_read_depth),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_framebuffer_read_depth_doc},
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
{"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
|
||||
#endif
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Ideally type aliases would de-duplicate:
|
||||
* `GPUTexture | dict[str, int | GPUTexture]` in this docstring. */
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer__tp_doc,
|
||||
".. class:: GPUFrameBuffer\n"
|
||||
"\n"
|
||||
" This object gives access to framebuffer functionalities.\n"
|
||||
" When a 'layer' is specified in a argument, a single layer of a 3D or array "
|
||||
"texture is attached to the frame-buffer.\n"
|
||||
" For cube map textures, layer is translated into a cube map face.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(*, depth_slot=None, color_slots=None)\n"
|
||||
"\n"
|
||||
" :param depth_slot: GPUTexture to attach or a ``dict`` containing keywords: "
|
||||
"'texture', 'layer' and 'mip'.\n"
|
||||
" :type depth_slot: :class:`gpu.types.GPUTexture` | dict[str, int | "
|
||||
":class:`gpu.types.GPUTexture`] | None\n"
|
||||
" :param color_slots: Tuple where each item can be a GPUTexture or a ``dict`` "
|
||||
"containing keywords: 'texture', 'layer' and 'mip'.\n"
|
||||
" :type color_slots: :class:`gpu.types.GPUTexture` | "
|
||||
"dict[str, int | :class:`gpu.types.GPUTexture`] | "
|
||||
"Sequence[:class:`gpu.types.GPUTexture` | dict[str, int | "
|
||||
":class:`gpu.types.GPUTexture`]] | "
|
||||
"None\n");
|
||||
PyTypeObject BPyGPUFrameBuffer_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUFrameBuffer",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUFrameBuffer),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(BPyGPUFrameBuffer__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_framebuffer__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_framebuffer__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_framebuffer__tp_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_framebuffer__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUFrameBuffer_CreatePyObject(gpu::FrameBuffer *fb, bool shared_reference)
|
||||
{
|
||||
BPyGPUFrameBuffer *self;
|
||||
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
if (shared_reference) {
|
||||
void **ref = GPU_framebuffer_py_reference_get(fb);
|
||||
if (ref) {
|
||||
/* Retrieve BPyGPUFrameBuffer reference. */
|
||||
self = reinterpret_cast<BPyGPUFrameBuffer *> POINTER_OFFSET(
|
||||
ref, -offsetof(BPyGPUFrameBuffer, fb));
|
||||
BLI_assert(self->fb == fb);
|
||||
Py_INCREF(self);
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(shared_reference);
|
||||
#endif
|
||||
|
||||
self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type);
|
||||
self->fb = fb;
|
||||
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
self->shared_reference = shared_reference;
|
||||
|
||||
BLI_assert(GPU_framebuffer_py_reference_get(fb) == nullptr);
|
||||
GPU_framebuffer_py_reference_set(fb, reinterpret_cast<void **>(&self->fb));
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef PYGPU_FRAMEBUFFER_CHECK_OBJ
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,37 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class FrameBuffer;
|
||||
} // namespace gpu
|
||||
|
||||
extern PyTypeObject BPyGPUFrameBuffer_Type;
|
||||
|
||||
#define BPyGPUFrameBuffer_Check(v) (Py_TYPE(v) == &BPyGPUFrameBuffer_Type)
|
||||
|
||||
struct BPyGPUFrameBuffer {
|
||||
PyObject_HEAD
|
||||
gpu::FrameBuffer *fb;
|
||||
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
bool shared_reference;
|
||||
#endif
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUFrameBuffer_CreatePyObject(gpu::FrameBuffer *fb,
|
||||
bool shared_reference) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
732
blender-5.2.0/source/blender/python/gpu/gpu_py_matrix.cc
Normal file
732
blender-5.2.0/source/blender/python/gpu/gpu_py_matrix.cc
Normal file
@@ -0,0 +1,732 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the gpu.matrix stack API.
|
||||
*
|
||||
* \warning While these functions attempt to ensure correct stack usage.
|
||||
* Mixing Python and C functions may still crash on invalid use.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#define USE_GPU_PY_MATRIX_API
|
||||
#include "GPU_matrix.hh"
|
||||
#undef USE_GPU_PY_MATRIX_API
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_matrix.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Helper Functions
|
||||
* \{ */
|
||||
|
||||
static bool pygpu_stack_is_push_model_view_ok_or_error()
|
||||
{
|
||||
if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"Maximum model-view stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_stack_is_push_projection_ok_or_error()
|
||||
{
|
||||
if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"Maximum projection stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_stack_is_pop_model_view_ok_or_error()
|
||||
{
|
||||
if (GPU_matrix_stack_level_get_model_view() == 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Minimum model-view stack depth reached");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_stack_is_pop_projection_ok_or_error()
|
||||
{
|
||||
if (GPU_matrix_stack_level_get_projection() == 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Minimum projection stack depth reached");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Manage Stack
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_push_doc,
|
||||
".. function:: push()\n"
|
||||
"\n"
|
||||
" Add to the model-view matrix stack.\n");
|
||||
static PyObject *pygpu_matrix_push(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!pygpu_stack_is_push_model_view_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_push();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_pop_doc,
|
||||
".. function:: pop()\n"
|
||||
"\n"
|
||||
" Remove the last model-view matrix from the stack.\n");
|
||||
static PyObject *pygpu_matrix_pop(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!pygpu_stack_is_pop_model_view_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_pop();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_push_projection_doc,
|
||||
".. function:: push_projection()\n"
|
||||
"\n"
|
||||
" Add to the projection matrix stack.\n");
|
||||
static PyObject *pygpu_matrix_push_projection(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!pygpu_stack_is_push_projection_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_push_projection();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_pop_projection_doc,
|
||||
".. function:: pop_projection()\n"
|
||||
"\n"
|
||||
" Remove the last projection matrix from the stack.\n");
|
||||
static PyObject *pygpu_matrix_pop_projection(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!pygpu_stack_is_pop_projection_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_pop_projection();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Stack (Context Manager)
|
||||
*
|
||||
* Safer alternative to ensure balanced push/pop calls.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct BPyGPU_MatrixStackContext {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
int type;
|
||||
int level;
|
||||
};
|
||||
|
||||
enum {
|
||||
PYGPU_MATRIX_TYPE_MODEL_VIEW = 1,
|
||||
PYGPU_MATRIX_TYPE_PROJECTION = 2,
|
||||
};
|
||||
|
||||
static PyObject *pygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self);
|
||||
static PyObject *pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args);
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_matrix_stack_context__tp_methods[] = {
|
||||
{"__enter__", reinterpret_cast<PyCFunction>(pygpu_matrix_stack_context_enter), METH_NOARGS},
|
||||
{"__exit__", reinterpret_cast<PyCFunction>(pygpu_matrix_stack_context_exit), METH_VARARGS},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_stack_context__tp_doc,
|
||||
"Context manager for matrix stack push/pop.");
|
||||
|
||||
PyTypeObject PyGPUMatrixStackContext_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "MatrixStackContext",
|
||||
/*tp_basicsize*/ sizeof(BPyGPU_MatrixStackContext),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ nullptr,
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_matrix_stack_context__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_matrix_stack_context__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
static PyObject *pygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
/* sanity - should never happen */
|
||||
if (self->level != -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Already in use");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
|
||||
if (!pygpu_stack_is_push_model_view_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_push();
|
||||
self->level = GPU_matrix_stack_level_get_model_view();
|
||||
}
|
||||
else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
|
||||
if (!pygpu_stack_is_push_projection_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_push_projection();
|
||||
self->level = GPU_matrix_stack_level_get_projection();
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self,
|
||||
PyObject * /*args*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
/* sanity - should never happen */
|
||||
if (self->level == -1) {
|
||||
fprintf(stderr, "Not yet in use\n");
|
||||
goto finally;
|
||||
}
|
||||
|
||||
if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
|
||||
const int level = GPU_matrix_stack_level_get_model_view();
|
||||
if (level != self->level) {
|
||||
fprintf(stderr, "Level push/pop mismatch, expected %d, got %d\n", self->level, level);
|
||||
}
|
||||
if (level != 0) {
|
||||
GPU_matrix_pop();
|
||||
}
|
||||
}
|
||||
else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
|
||||
const int level = GPU_matrix_stack_level_get_projection();
|
||||
if (level != self->level) {
|
||||
fprintf(stderr, "Level push/pop mismatch, expected %d, got %d", self->level, level);
|
||||
}
|
||||
if (level != 0) {
|
||||
GPU_matrix_pop_projection();
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
finally:
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_matrix_push_pop_impl(int type)
|
||||
{
|
||||
BPyGPU_MatrixStackContext *ret = PyObject_New(BPyGPU_MatrixStackContext,
|
||||
&PyGPUMatrixStackContext_Type);
|
||||
ret->type = type;
|
||||
ret->level = -1;
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_push_pop_doc,
|
||||
".. function:: push_pop()\n"
|
||||
"\n"
|
||||
" Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
|
||||
"\n"
|
||||
" :return: The context manager.\n"
|
||||
" :rtype: :class:`gpu.types.MatrixStackContext`\n");
|
||||
static PyObject *pygpu_matrix_push_pop(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_push_pop_projection_doc,
|
||||
".. function:: push_pop_projection()\n"
|
||||
"\n"
|
||||
" Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
|
||||
"\n"
|
||||
" :return: The context manager.\n"
|
||||
" :rtype: :class:`gpu.types.MatrixStackContext`\n");
|
||||
static PyObject *pygpu_matrix_push_pop_projection(PyObject * /*self*/)
|
||||
{
|
||||
return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Manipulate State
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_multiply_matrix_doc,
|
||||
".. function:: multiply_matrix(matrix)\n"
|
||||
"\n"
|
||||
" Multiply the current stack matrix.\n"
|
||||
"\n"
|
||||
" :param matrix: A 4x4 matrix.\n"
|
||||
" :type matrix: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_multiply_matrix(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
MatrixObject *pymat;
|
||||
if (!Matrix_Parse4x4(value, &pymat)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_mul(pymat->matrix);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_scale_doc,
|
||||
".. function:: scale(scale)\n"
|
||||
"\n"
|
||||
" Scale the current stack matrix.\n"
|
||||
"\n"
|
||||
" :param scale: Scale the current stack matrix with 2 or 3 floats.\n"
|
||||
" :type scale: Sequence[float]\n");
|
||||
static PyObject *pygpu_matrix_scale(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float scale[3];
|
||||
int len;
|
||||
if ((len = mathutils_array_parse(
|
||||
scale, 2, 3, value, "gpu.matrix.scale(): invalid vector arg")) == -1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (len == 2) {
|
||||
GPU_matrix_scale_2fv(scale);
|
||||
}
|
||||
else {
|
||||
GPU_matrix_scale_3fv(scale);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_scale_uniform_doc,
|
||||
".. function:: scale_uniform(scale)\n"
|
||||
"\n"
|
||||
" Scale the current stack matrix uniformly.\n"
|
||||
"\n"
|
||||
" :param scale: Uniform scale factor.\n"
|
||||
" :type scale: float\n");
|
||||
static PyObject *pygpu_matrix_scale_uniform(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float scalar;
|
||||
if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError, "expected a number, not %.200s", Py_TYPE(value)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_scale_1f(scalar);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_translate_doc,
|
||||
".. function:: translate(offset)\n"
|
||||
"\n"
|
||||
" Translate the current stack matrix.\n"
|
||||
"\n"
|
||||
" :param offset: Translate the current stack matrix with 2 or 3 floats.\n"
|
||||
" :type offset: Sequence[float]\n");
|
||||
static PyObject *pygpu_matrix_translate(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
float offset[3];
|
||||
int len;
|
||||
if ((len = mathutils_array_parse(
|
||||
offset, 2, 3, value, "gpu.matrix.translate(): invalid vector arg")) == -1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (len == 2) {
|
||||
GPU_matrix_translate_2fv(offset);
|
||||
}
|
||||
else {
|
||||
GPU_matrix_translate_3fv(offset);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Write State
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_reset_doc,
|
||||
".. function:: reset()\n"
|
||||
"\n"
|
||||
" Empty stack and set to identity.\n");
|
||||
static PyObject *pygpu_matrix_reset(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPU_matrix_reset();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_load_identity_doc,
|
||||
".. function:: load_identity()\n"
|
||||
"\n"
|
||||
" Load an identity matrix into the stack.\n");
|
||||
static PyObject *pygpu_matrix_load_identity(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPU_matrix_identity_set();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_load_matrix_doc,
|
||||
".. function:: load_matrix(matrix)\n"
|
||||
"\n"
|
||||
" Load a matrix into the stack.\n"
|
||||
"\n"
|
||||
" :param matrix: A 4x4 matrix.\n"
|
||||
" :type matrix: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_load_matrix(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
MatrixObject *pymat;
|
||||
if (!Matrix_Parse4x4(value, &pymat)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_set(pymat->matrix);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_load_projection_matrix_doc,
|
||||
".. function:: load_projection_matrix(matrix)\n"
|
||||
"\n"
|
||||
" Load a projection matrix into the stack.\n"
|
||||
"\n"
|
||||
" :param matrix: A 4x4 matrix.\n"
|
||||
" :type matrix: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_load_projection_matrix(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
MatrixObject *pymat;
|
||||
if (!Matrix_Parse4x4(value, &pymat)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_projection_set(pymat->matrix);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Read State
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_get_projection_matrix_doc,
|
||||
".. function:: get_projection_matrix()\n"
|
||||
"\n"
|
||||
" Return a copy of the projection matrix.\n"
|
||||
"\n"
|
||||
" :return: A 4x4 projection matrix.\n"
|
||||
" :rtype: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_get_projection_matrix(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float matrix[4][4];
|
||||
GPU_matrix_projection_get(matrix);
|
||||
return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_get_model_view_matrix_doc,
|
||||
".. function:: get_model_view_matrix()\n"
|
||||
"\n"
|
||||
" Return a copy of the model-view matrix.\n"
|
||||
"\n"
|
||||
" :return: A 4x4 view matrix.\n"
|
||||
" :rtype: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_get_model_view_matrix(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float matrix[4][4];
|
||||
GPU_matrix_model_view_get(matrix);
|
||||
return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_get_normal_matrix_doc,
|
||||
".. function:: get_normal_matrix()\n"
|
||||
"\n"
|
||||
" Return a copy of the normal matrix.\n"
|
||||
"\n"
|
||||
" :return: A 3x3 normal matrix.\n"
|
||||
" :rtype: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_get_normal_matrix(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float matrix[3][3];
|
||||
GPU_matrix_normal_get(matrix);
|
||||
return Matrix_CreatePyObject(&matrix[0][0], 3, 3, nullptr);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_matrix__tp_methods[] = {
|
||||
/* Manage Stack */
|
||||
{"push", reinterpret_cast<PyCFunction>(pygpu_matrix_push), METH_NOARGS, pygpu_matrix_push_doc},
|
||||
{"pop", reinterpret_cast<PyCFunction>(pygpu_matrix_pop), METH_NOARGS, pygpu_matrix_pop_doc},
|
||||
|
||||
{"push_projection",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_push_projection),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_push_projection_doc},
|
||||
{"pop_projection",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_pop_projection),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_pop_projection_doc},
|
||||
|
||||
/* Stack (Context Manager) */
|
||||
{"push_pop",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_push_pop),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_push_pop_doc},
|
||||
{"push_pop_projection",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_push_pop_projection),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_push_pop_projection_doc},
|
||||
|
||||
/* Manipulate State */
|
||||
{"multiply_matrix",
|
||||
static_cast<PyCFunction>(pygpu_matrix_multiply_matrix),
|
||||
METH_O,
|
||||
pygpu_matrix_multiply_matrix_doc},
|
||||
{"scale", static_cast<PyCFunction>(pygpu_matrix_scale), METH_O, pygpu_matrix_scale_doc},
|
||||
{"scale_uniform",
|
||||
static_cast<PyCFunction>(pygpu_matrix_scale_uniform),
|
||||
METH_O,
|
||||
pygpu_matrix_scale_uniform_doc},
|
||||
{"translate",
|
||||
static_cast<PyCFunction>(pygpu_matrix_translate),
|
||||
METH_O,
|
||||
pygpu_matrix_translate_doc},
|
||||
|
||||
/* TODO */
|
||||
#if 0
|
||||
{"rotate", (PyCFunction)pygpu_matrix_rotate, METH_O, pygpu_matrix_rotate_doc},
|
||||
{"rotate_axis", (PyCFunction)pygpu_matrix_rotate_axis, METH_O, pygpu_matrix_rotate_axis_doc},
|
||||
{"look_at", (PyCFunction)pygpu_matrix_look_at, METH_O, pygpu_matrix_look_at_doc},
|
||||
#endif
|
||||
|
||||
/* Write State */
|
||||
{"reset",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_reset),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_reset_doc},
|
||||
{"load_identity",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_load_identity),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_load_identity_doc},
|
||||
{"load_matrix",
|
||||
static_cast<PyCFunction>(pygpu_matrix_load_matrix),
|
||||
METH_O,
|
||||
pygpu_matrix_load_matrix_doc},
|
||||
{"load_projection_matrix",
|
||||
static_cast<PyCFunction>(pygpu_matrix_load_projection_matrix),
|
||||
METH_O,
|
||||
pygpu_matrix_load_projection_matrix_doc},
|
||||
|
||||
/* Read State */
|
||||
{"get_projection_matrix",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_get_projection_matrix),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_get_projection_matrix_doc},
|
||||
{"get_model_view_matrix",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_get_model_view_matrix),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_get_model_view_matrix_doc},
|
||||
{"get_normal_matrix",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_get_normal_matrix),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_get_normal_matrix_doc},
|
||||
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix__tp_doc,
|
||||
"This module provides access to the matrix stack.");
|
||||
static PyModuleDef pygpu_matrix_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.matrix",
|
||||
/*m_doc*/ pygpu_matrix__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_matrix__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_matrix_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_matrix_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
19
blender-5.2.0/source/blender/python/gpu/gpu_py_matrix.hh
Normal file
19
blender-5.2.0/source/blender/python/gpu/gpu_py_matrix.hh
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern PyTypeObject PyGPUMatrixStackContext_Type;
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_matrix_init();
|
||||
|
||||
} // namespace blender
|
||||
689
blender-5.2.0/source/blender/python/gpu/gpu_py_offscreen.cc
Normal file
689
blender-5.2.0/source/blender/python/gpu/gpu_py_offscreen.cc
Normal file
@@ -0,0 +1,689 @@
|
||||
/* SPDX-FileCopyrightText: 2015 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \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 "BLI_string_utf8.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_lib_id.hh" /* For #BKE_id_is_in_global_main. */
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_viewport.hh"
|
||||
|
||||
#include "ED_view3d_offscreen.hh"
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_texture.hh"
|
||||
|
||||
#include "gpu_py_offscreen.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Define the free method to avoid breakage. */
|
||||
#define BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUOffScreen Common Utilities
|
||||
* \{ */
|
||||
|
||||
static const PyC_StringEnumItems pygpu_framebuffer_color_texture_formats[] = {
|
||||
{int(gpu::TextureFormat::UNORM_8_8_8_8), "RGBA8"},
|
||||
{int(gpu::TextureFormat::UNORM_16_16_16_16), "RGBA16"},
|
||||
{int(gpu::TextureFormat::SFLOAT_16_16_16_16), "RGBA16F"},
|
||||
{int(gpu::TextureFormat::SFLOAT_32_32_32_32), "RGBA32F"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
static int pygpu_offscreen_valid_check(BPyGPUOffScreen *py_ofs)
|
||||
{
|
||||
if (UNLIKELY(py_ofs->ofs == nullptr)) {
|
||||
PyErr_SetString(PyExc_ReferenceError,
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
"GPU offscreen was freed, no further access is valid"
|
||||
#else
|
||||
"GPU offscreen: internal error"
|
||||
#endif
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BPY_GPU_OFFSCREEN_CHECK_OBJ(bpygpu) \
|
||||
{ \
|
||||
if (UNLIKELY(pygpu_offscreen_valid_check(bpygpu) == -1)) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Stack (Context Manager)
|
||||
*
|
||||
* Safer alternative to ensure balanced push/pop calls.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct OffScreenStackContext {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
BPyGPUOffScreen *py_offscreen;
|
||||
int level;
|
||||
bool is_explicitly_bound; /* Bound by "bind" method. */
|
||||
};
|
||||
|
||||
static void pygpu_offscreen_stack_context__tp_dealloc(OffScreenStackContext *self)
|
||||
{
|
||||
Py_DECREF(self->py_offscreen);
|
||||
PyObject_DEL(self);
|
||||
}
|
||||
|
||||
static PyObject *pygpu_offscreen_stack_context_enter(OffScreenStackContext *self)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offscreen);
|
||||
|
||||
if (!self->is_explicitly_bound) {
|
||||
if (self->level != -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Already in use");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_offscreen_bind(self->py_offscreen->ofs, true);
|
||||
self->level = GPU_framebuffer_stack_level_get();
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_offscreen_stack_context_exit(OffScreenStackContext *self,
|
||||
PyObject * /*args*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offscreen);
|
||||
|
||||
if (self->level == -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Not yet in use\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int level = GPU_framebuffer_stack_level_get();
|
||||
if (level != self->level) {
|
||||
PyErr_Format(
|
||||
PyExc_RuntimeError, "Level of bind mismatch, expected %d, got %d\n", self->level, level);
|
||||
}
|
||||
|
||||
GPU_offscreen_unbind(self->py_offscreen->ofs, true);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_offscreen_stack_context__tp_methods[] = {
|
||||
{"__enter__", reinterpret_cast<PyCFunction>(pygpu_offscreen_stack_context_enter), METH_NOARGS},
|
||||
{"__exit__", reinterpret_cast<PyCFunction>(pygpu_offscreen_stack_context_exit), METH_VARARGS},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_stack_context__tp_doc,
|
||||
"Context manager for off-screen framebuffer binding.");
|
||||
PyTypeObject PyGPUOffscreenStackContext_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "OffScreenStackContext",
|
||||
/*tp_basicsize*/ sizeof(OffScreenStackContext),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_offscreen_stack_context__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_offscreen_stack_context__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_offscreen_stack_context__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_bind_doc,
|
||||
".. method:: bind()\n"
|
||||
"\n"
|
||||
" Context manager to ensure balanced bind calls, even in the case of an error.\n"
|
||||
"\n"
|
||||
" :return: A context manager for the off-screen binding.\n"
|
||||
" :rtype: :class:`gpu.types.OffScreenStackContext`\n");
|
||||
static PyObject *pygpu_offscreen_bind(BPyGPUOffScreen *self)
|
||||
{
|
||||
OffScreenStackContext *ret = PyObject_New(OffScreenStackContext,
|
||||
&PyGPUOffscreenStackContext_Type);
|
||||
ret->py_offscreen = self;
|
||||
ret->level = -1;
|
||||
ret->is_explicitly_bound = false;
|
||||
Py_INCREF(self);
|
||||
|
||||
pygpu_offscreen_stack_context_enter(ret);
|
||||
ret->is_explicitly_bound = true;
|
||||
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_unbind_doc,
|
||||
".. method:: unbind(*, restore=True)\n"
|
||||
"\n"
|
||||
" Unbind the offscreen object.\n"
|
||||
"\n"
|
||||
" :param restore: Restore the OpenGL state, can only be used when the state has been "
|
||||
"saved before.\n"
|
||||
" :type restore: bool\n");
|
||||
static PyObject *pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
bool restore = true;
|
||||
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
|
||||
static const char *_keywords[] = {"restore", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `restore` */
|
||||
":unbind",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &restore)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_offscreen_unbind(self->ofs, restore);
|
||||
GPU_apply_state();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUOffscreen Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_offscreen__tp_new(PyTypeObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPUOffScreen *ofs = nullptr;
|
||||
int width, height;
|
||||
PyC_StringEnum pygpu_textureformat = {pygpu_framebuffer_color_texture_formats,
|
||||
int(gpu::TextureFormat::UNORM_8_8_8_8)};
|
||||
char err_out[256];
|
||||
|
||||
static const char *_keywords[] = {"width", "height", "format", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"i" /* `width` */
|
||||
"i" /* `height` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `format` */
|
||||
":GPUOffScreen.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &width, &height, PyC_ParseStringEnum, &pygpu_textureformat))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (GPU_context_active_get()) {
|
||||
ofs = GPU_offscreen_create(width,
|
||||
height,
|
||||
true,
|
||||
gpu::TextureFormat(pygpu_textureformat.value_found),
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_HOST_READ,
|
||||
false,
|
||||
err_out);
|
||||
}
|
||||
else {
|
||||
STRNCPY_UTF8(err_out, "No active GPU context found");
|
||||
}
|
||||
|
||||
if (ofs == nullptr) {
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"gpu.offscreen.new(...) failed with '%s'",
|
||||
err_out[0] ? err_out : "unknown error");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return BPyGPUOffScreen_CreatePyObject(ofs);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_width_doc,
|
||||
"Width of the texture.\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
static PyObject *pygpu_offscreen_width_get(BPyGPUOffScreen *self, void * /*type*/)
|
||||
{
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
return PyLong_FromLong(GPU_offscreen_width(self->ofs));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_height_doc,
|
||||
"Height of the texture.\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
static PyObject *pygpu_offscreen_height_get(BPyGPUOffScreen *self, void * /*type*/)
|
||||
{
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
return PyLong_FromLong(GPU_offscreen_height(self->ofs));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_texture_color_doc,
|
||||
"The color texture attached.\n"
|
||||
"\n"
|
||||
":type: :class:`gpu.types.GPUTexture`\n");
|
||||
static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void * /*type*/)
|
||||
{
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
gpu::Texture *texture = GPU_offscreen_color_texture(self->ofs);
|
||||
return BPyGPUTexture_CreatePyObject(texture, true);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_draw_view3d_doc,
|
||||
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, "
|
||||
"*, do_color_management=False, draw_background=True)\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 view_layer: View layer to draw.\n"
|
||||
" :type view_layer: :class:`bpy.types.ViewLayer`\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 (required as temporary draw target).\n"
|
||||
" :type region: :class:`bpy.types.Region`\n"
|
||||
" :param view_matrix: View Matrix (e.g. ``camera.matrix_world.inverted()``).\n"
|
||||
" :type view_matrix: :class:`mathutils.Matrix`\n"
|
||||
" :param projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n"
|
||||
" :type projection_matrix: :class:`mathutils.Matrix`\n"
|
||||
" :param do_color_management: Color manage the output.\n"
|
||||
" :type do_color_management: bool\n"
|
||||
" :param draw_background: Draw background.\n"
|
||||
" :type draw_background: bool\n");
|
||||
static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
MatrixObject *py_mat_view, *py_mat_projection;
|
||||
PyObject *py_scene, *py_view_layer, *py_region, *py_view3d;
|
||||
|
||||
Depsgraph *depsgraph;
|
||||
Scene *scene;
|
||||
ViewLayer *view_layer;
|
||||
View3D *v3d;
|
||||
ARegion *region;
|
||||
|
||||
bool do_color_management = false;
|
||||
bool draw_background = true;
|
||||
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
|
||||
static const char *_keywords[] = {
|
||||
"scene",
|
||||
"view_layer",
|
||||
"view3d",
|
||||
"region",
|
||||
"view_matrix",
|
||||
"projection_matrix",
|
||||
"do_color_management",
|
||||
"draw_background",
|
||||
nullptr,
|
||||
};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O" /* `scene` */
|
||||
"O" /* `view_layer` */
|
||||
"O" /* `view3d` */
|
||||
"O" /* `region` */
|
||||
"O&" /* `view_matrix` */
|
||||
"O&" /* `projection_matrix` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `do_color_management` */
|
||||
"O&" /* `draw_background` */
|
||||
":draw_view3d",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kwds,
|
||||
&_parser,
|
||||
&py_scene,
|
||||
&py_view_layer,
|
||||
&py_view3d,
|
||||
&py_region,
|
||||
Matrix_Parse4x4,
|
||||
&py_mat_view,
|
||||
Matrix_Parse4x4,
|
||||
&py_mat_projection,
|
||||
PyC_ParseBool,
|
||||
&do_color_management,
|
||||
PyC_ParseBool,
|
||||
&draw_background) ||
|
||||
(!(scene = static_cast<Scene *>(PyC_RNA_AsPointer(py_scene, "Scene"))) ||
|
||||
!(view_layer = static_cast<ViewLayer *>(PyC_RNA_AsPointer(py_view_layer, "ViewLayer"))) ||
|
||||
!(v3d = static_cast<View3D *>(PyC_RNA_AsPointer(py_view3d, "SpaceView3D"))) ||
|
||||
!(region = static_cast<ARegion *>(PyC_RNA_AsPointer(py_region, "Region")))))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ED_view3d_draw_offscreen_check_nested()) {
|
||||
/* NOTE(@ideasman42): Nested draw calls could be supported.
|
||||
* Adding support for this looks to be possible but non-trivial. */
|
||||
PyErr_SetString(PyExc_RuntimeError, "Nested off-screen drawing not supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(BKE_id_is_in_global_main(&scene->id));
|
||||
|
||||
depsgraph = BKE_scene_ensure_depsgraph(G_MAIN, scene, view_layer);
|
||||
|
||||
GPU_offscreen_bind(self->ofs, true);
|
||||
|
||||
/* Cache the #GPUViewport so the frame-buffers and associated textures are
|
||||
* not reallocated each time, see: #89204 */
|
||||
if (!self->viewport) {
|
||||
self->viewport = GPU_viewport_create();
|
||||
}
|
||||
else {
|
||||
GPU_viewport_tag_update(self->viewport);
|
||||
}
|
||||
|
||||
ED_view3d_draw_offscreen(depsgraph,
|
||||
scene,
|
||||
eDrawType(v3d->shading.type),
|
||||
v3d,
|
||||
region,
|
||||
nullptr,
|
||||
GPU_offscreen_width(self->ofs),
|
||||
GPU_offscreen_height(self->ofs),
|
||||
reinterpret_cast<const float (*)[4]>(py_mat_view->matrix),
|
||||
reinterpret_cast<const float (*)[4]>(py_mat_projection->matrix),
|
||||
true,
|
||||
draw_background,
|
||||
"",
|
||||
do_color_management,
|
||||
true,
|
||||
self->ofs,
|
||||
self->viewport);
|
||||
|
||||
GPU_offscreen_unbind(self->ofs, true);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_free_doc,
|
||||
".. method:: 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(BPyGPUOffScreen *self)
|
||||
{
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
|
||||
if (self->viewport) {
|
||||
GPU_viewport_free(self->viewport);
|
||||
self->viewport = nullptr;
|
||||
}
|
||||
|
||||
GPU_offscreen_free(self->ofs);
|
||||
self->ofs = nullptr;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void BPyGPUOffScreen__tp_dealloc(BPyGPUOffScreen *self)
|
||||
{
|
||||
if (self->viewport) {
|
||||
GPU_viewport_free(self->viewport);
|
||||
}
|
||||
if (self->ofs) {
|
||||
GPU_offscreen_free(self->ofs);
|
||||
}
|
||||
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject *>(self));
|
||||
}
|
||||
|
||||
static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
|
||||
{"texture_color",
|
||||
reinterpret_cast<getter>(pygpu_offscreen_texture_color_get),
|
||||
static_cast<setter>(nullptr),
|
||||
pygpu_offscreen_texture_color_doc,
|
||||
nullptr},
|
||||
{"width",
|
||||
reinterpret_cast<getter>(pygpu_offscreen_width_get),
|
||||
static_cast<setter>(nullptr),
|
||||
pygpu_offscreen_width_doc,
|
||||
nullptr},
|
||||
{"height",
|
||||
reinterpret_cast<getter>(pygpu_offscreen_height_get),
|
||||
static_cast<setter>(nullptr),
|
||||
pygpu_offscreen_height_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_offscreen__tp_methods[] = {
|
||||
{"bind",
|
||||
reinterpret_cast<PyCFunction>(pygpu_offscreen_bind),
|
||||
METH_NOARGS,
|
||||
pygpu_offscreen_bind_doc},
|
||||
{"unbind",
|
||||
reinterpret_cast<PyCFunction>(pygpu_offscreen_unbind),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_offscreen_unbind_doc},
|
||||
{"draw_view3d",
|
||||
reinterpret_cast<PyCFunction>(pygpu_offscreen_draw_view3d),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_offscreen_draw_view3d_doc},
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
{"free",
|
||||
reinterpret_cast<PyCFunction>(pygpu_offscreen_free),
|
||||
METH_NOARGS,
|
||||
pygpu_offscreen_free_doc},
|
||||
#endif
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen__tp_doc,
|
||||
".. class:: GPUOffScreen\n"
|
||||
"\n"
|
||||
" This object gives access to off screen buffers.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(width, height, *, format='RGBA8')\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 format: Internal data format inside GPU memory for color attachment texture.\n"
|
||||
" :type format: Literal['RGBA8', 'RGBA16', 'RGBA16F', 'RGBA32F']\n");
|
||||
PyTypeObject BPyGPUOffScreen_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUOffScreen",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUOffScreen),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(BPyGPUOffScreen__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_compare*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_offscreen__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_offscreen__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_offscreen__tp_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_offscreen__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUOffScreen_CreatePyObject(GPUOffScreen *ofs)
|
||||
{
|
||||
BPyGPUOffScreen *self;
|
||||
|
||||
self = PyObject_New(BPyGPUOffScreen, &BPyGPUOffScreen_Type);
|
||||
self->ofs = ofs;
|
||||
self->viewport = nullptr;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef BPY_GPU_OFFSCREEN_CHECK_OBJ
|
||||
|
||||
} // namespace blender
|
||||
33
blender-5.2.0/source/blender/python/gpu/gpu_py_offscreen.hh
Normal file
33
blender-5.2.0/source/blender/python/gpu/gpu_py_offscreen.hh
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUOffScreen;
|
||||
struct GPUViewport;
|
||||
|
||||
extern PyTypeObject BPyGPUOffScreen_Type;
|
||||
extern PyTypeObject PyGPUOffscreenStackContext_Type;
|
||||
|
||||
#define BPyGPUOffScreen_Check(v) (Py_TYPE(v) == &BPyGPUOffScreen_Type)
|
||||
|
||||
struct BPyGPUOffScreen {
|
||||
PyObject_HEAD
|
||||
GPUOffScreen *ofs;
|
||||
GPUViewport *viewport;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUOffScreen_CreatePyObject(GPUOffScreen *ofs) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
419
blender-5.2.0/source/blender/python/gpu/gpu_py_platform.cc
Normal file
419
blender-5.2.0/source/blender/python/gpu/gpu_py_platform.cc
Normal file
@@ -0,0 +1,419 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_platform.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Device Type
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(pygpu_device_index_doc,
|
||||
"Device index.\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
static PyObject *pygpu_device_index_get(BPyGPUDevice *self, void * /*closure*/)
|
||||
{
|
||||
return PyLong_FromLong(self->index);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_device_identifier_doc,
|
||||
"Device identifier.\n"
|
||||
"\n"
|
||||
":type: str\n");
|
||||
static PyObject *pygpu_device_identifier_get(BPyGPUDevice *self, void * /*closure*/)
|
||||
{
|
||||
return PyUnicode_FromString(self->identifier);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_device_name_doc,
|
||||
"Device name.\n"
|
||||
"\n"
|
||||
":type: str\n");
|
||||
static PyObject *pygpu_device_name_get(BPyGPUDevice *self, void * /*closure*/)
|
||||
{
|
||||
return PyUnicode_FromString(self->name);
|
||||
}
|
||||
|
||||
/* Property descriptors */
|
||||
static PyGetSetDef pygpu_device_getseters[] = {
|
||||
{"index",
|
||||
reinterpret_cast<getter>(pygpu_device_index_get),
|
||||
nullptr,
|
||||
pygpu_device_index_doc,
|
||||
nullptr},
|
||||
{"identifier",
|
||||
reinterpret_cast<getter>(pygpu_device_identifier_get),
|
||||
nullptr,
|
||||
pygpu_device_identifier_doc,
|
||||
nullptr},
|
||||
{"name",
|
||||
reinterpret_cast<getter>(pygpu_device_name_get),
|
||||
nullptr,
|
||||
pygpu_device_name_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
};
|
||||
|
||||
/* Representation */
|
||||
static PyObject *pygpu_device__tp_repr(BPyGPUDevice *self)
|
||||
{
|
||||
return PyUnicode_FromFormat("<GPUDevice index=%d identifier=\"%s\" name=\"%s\">",
|
||||
self->index,
|
||||
self->identifier,
|
||||
self->name);
|
||||
}
|
||||
|
||||
/** Rich comparison for GPUDevice types, compares by index. */
|
||||
static PyObject *pygpu_device__tp_richcmp(BPyGPUDevice *self, PyObject *other, int op)
|
||||
{
|
||||
if (!Py_IS_TYPE(other, &BPyGPU_DeviceType)) {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
BPyGPUDevice *other_device = reinterpret_cast<BPyGPUDevice *>(other);
|
||||
switch (op) {
|
||||
case Py_LT:
|
||||
return PyBool_FromLong(self->index < other_device->index);
|
||||
case Py_LE:
|
||||
return PyBool_FromLong(self->index <= other_device->index);
|
||||
case Py_EQ:
|
||||
return PyBool_FromLong(self->index == other_device->index);
|
||||
case Py_NE:
|
||||
return PyBool_FromLong(self->index != other_device->index);
|
||||
case Py_GT:
|
||||
return PyBool_FromLong(self->index > other_device->index);
|
||||
case Py_GE:
|
||||
return PyBool_FromLong(self->index >= other_device->index);
|
||||
}
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
/* Type definition */
|
||||
PyDoc_STRVAR(pygpu_device__tp_doc,
|
||||
".. class:: GPUDevice\n"
|
||||
"\n"
|
||||
" Represents a GPU device.\n"
|
||||
"\n"
|
||||
" :ivar index: Device index.\n"
|
||||
" :type index: int\n"
|
||||
" :ivar identifier: Device identifier.\n"
|
||||
" :type identifier: str\n"
|
||||
" :ivar name: Device name.\n"
|
||||
" :type name: str\n");
|
||||
|
||||
PyTypeObject BPyGPU_DeviceType = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "gpu.platform.GPUDevice",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUDevice),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ nullptr,
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_compare*/ nullptr,
|
||||
/*tp_repr*/ reinterpret_cast<reprfunc>(pygpu_device__tp_repr),
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_device__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ reinterpret_cast<richcmpfunc>(pygpu_device__tp_richcmp),
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ nullptr,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_device_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
static BPyGPUDevice *pygpu_device_new(int index, const char *identifier, const char *name)
|
||||
{
|
||||
BPyGPUDevice *self = reinterpret_cast<BPyGPUDevice *>(
|
||||
BPyGPU_DeviceType.tp_alloc(&BPyGPU_DeviceType, 0));
|
||||
if (self != nullptr) {
|
||||
self->index = index;
|
||||
self->identifier = identifier;
|
||||
self->name = name;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Functions
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_vendor_get_doc,
|
||||
".. function:: vendor_get()\n"
|
||||
"\n"
|
||||
" Get GPU vendor.\n"
|
||||
"\n"
|
||||
" :return: Vendor name.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_vendor_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyUnicode_FromString(GPU_platform_vendor());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_renderer_get_doc,
|
||||
".. function:: renderer_get()\n"
|
||||
"\n"
|
||||
" Get GPU to be used for rendering.\n"
|
||||
"\n"
|
||||
" :return: GPU name.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_renderer_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyUnicode_FromString(GPU_platform_renderer());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_version_get_doc,
|
||||
".. function:: version_get()\n"
|
||||
"\n"
|
||||
" Get GPU driver version.\n"
|
||||
"\n"
|
||||
" :return: Driver version.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_version_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyUnicode_FromString(GPU_platform_version());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_device_type_get_doc,
|
||||
".. function:: device_type_get()\n"
|
||||
"\n"
|
||||
" Get GPU device type.\n"
|
||||
"\n"
|
||||
" :return: Device type ('APPLE', 'NVIDIA', 'AMD', 'INTEL', 'SOFTWARE', 'QUALCOMM', "
|
||||
"'UNKNOWN').\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_device_type_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
const char *device;
|
||||
if (GPU_type_matches(GPU_DEVICE_APPLE, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "APPLE";
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "NVIDIA";
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "AMD";
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "INTEL";
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "SOFTWARE";
|
||||
}
|
||||
/* Right now we can only detect Qualcomm GPUs on Windows, not other OSes */
|
||||
else if (GPU_type_matches(GPU_DEVICE_QUALCOMM, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
device = "QUALCOMM";
|
||||
}
|
||||
else {
|
||||
device = "UNKNOWN";
|
||||
}
|
||||
return PyUnicode_FromString(device);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_backend_type_get_doc,
|
||||
".. function:: backend_type_get()\n"
|
||||
"\n"
|
||||
" Get active GPU backend.\n"
|
||||
"\n"
|
||||
" :return: Backend type ('OPENGL', 'VULKAN', 'METAL', 'NONE', 'UNKNOWN').\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_backend_type_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
const char *backend = "UNKNOWN";
|
||||
switch (GPU_backend_get_type()) {
|
||||
case GPU_BACKEND_VULKAN: {
|
||||
backend = "VULKAN";
|
||||
break;
|
||||
}
|
||||
case GPU_BACKEND_METAL: {
|
||||
backend = "METAL";
|
||||
break;
|
||||
}
|
||||
case GPU_BACKEND_NONE: {
|
||||
backend = "NONE";
|
||||
break;
|
||||
}
|
||||
case GPU_BACKEND_OPENGL: {
|
||||
backend = "OPENGL";
|
||||
break;
|
||||
}
|
||||
case GPU_BACKEND_ANY:
|
||||
break;
|
||||
}
|
||||
return PyUnicode_FromString(backend);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_devices_get_doc,
|
||||
".. function:: devices_get()\n"
|
||||
"\n"
|
||||
" Get all available GPU devices.\n"
|
||||
"\n"
|
||||
" :return: List of :class:`GPUDevice` objects for each device.\n"
|
||||
" :rtype: list\n");
|
||||
static PyObject *pygpu_platform_devices_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
Span<GPUDevice> devices = GPU_platform_devices_list();
|
||||
PyObject *list = PyList_New(devices.size());
|
||||
for (int i = 0; i < devices.size(); i++) {
|
||||
const GPUDevice &dev = devices[i];
|
||||
PyObject *item = reinterpret_cast<PyObject *>(
|
||||
pygpu_device_new(dev.index, dev.identifier.c_str(), dev.name.c_str()));
|
||||
PyList_SET_ITEM(list, i, item);
|
||||
}
|
||||
/* Sort by index (first attribute) for deterministic ordering. */
|
||||
PyList_Sort(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_platform__tp_methods[] = {
|
||||
{"vendor_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_vendor_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_vendor_get_doc},
|
||||
{"renderer_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_renderer_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_renderer_get_doc},
|
||||
{"version_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_version_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_version_get_doc},
|
||||
{"device_type_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_device_type_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_device_type_get_doc},
|
||||
{"backend_type_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_backend_type_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_backend_type_get_doc},
|
||||
{"devices_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_devices_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_devices_get_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform__tp_doc,
|
||||
"This module provides access to GPU Platform definitions.");
|
||||
static PyModuleDef pygpu_platform_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.platform",
|
||||
/*m_doc*/ pygpu_platform__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_platform__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_platform_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_platform_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
27
blender-5.2.0/source/blender/python/gpu/gpu_py_platform.hh
Normal file
27
blender-5.2.0/source/blender/python/gpu/gpu_py_platform.hh
Normal file
@@ -0,0 +1,27 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* GPU Device Python object structure */
|
||||
struct BPyGPUDevice {
|
||||
PyObject_HEAD
|
||||
int index;
|
||||
const char *identifier;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
extern PyTypeObject BPyGPU_DeviceType;
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_platform_init();
|
||||
|
||||
} // namespace blender
|
||||
92
blender-5.2.0/source/blender/python/gpu/gpu_py_select.cc
Normal file
92
blender-5.2.0/source/blender/python/gpu/gpu_py_select.cc
Normal file
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the gpu.select API.
|
||||
*
|
||||
* \note Currently only used for gizmo selection,
|
||||
* will need to add begin/end and a way to access the hits.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "GPU_select.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_select.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Methods
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_select_load_id_doc,
|
||||
".. function:: load_id(id)\n"
|
||||
"\n"
|
||||
" Set the selection ID.\n"
|
||||
"\n"
|
||||
" :param id: Number (32-bit uint).\n"
|
||||
" :type id: int\n");
|
||||
static PyObject *pygpu_select_load_id(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
uint id;
|
||||
if ((id = PyC_Long_AsU32(value)) == uint(-1)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_select_load_id(id);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
static PyMethodDef pygpu_select__tp_methods[] = {
|
||||
/* Manage Stack */
|
||||
{"load_id", static_cast<PyCFunction>(pygpu_select_load_id), METH_O, pygpu_select_load_id_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_select__tp_doc,
|
||||
"This module provides access to selection.");
|
||||
static PyModuleDef pygpu_select_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.select",
|
||||
/*m_doc*/ pygpu_select__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_select__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_select_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_select_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/gpu/gpu_py_select.hh
Normal file
17
blender-5.2.0/source/blender/python/gpu/gpu_py_select.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_select_init();
|
||||
|
||||
} // namespace blender
|
||||
1138
blender-5.2.0/source/blender/python/gpu/gpu_py_shader.cc
Normal file
1138
blender-5.2.0/source/blender/python/gpu/gpu_py_shader.cc
Normal file
File diff suppressed because it is too large
Load Diff
79
blender-5.2.0/source/blender/python/gpu/gpu_py_shader.hh
Normal file
79
blender-5.2.0/source/blender/python/gpu/gpu_py_shader.hh
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
struct GPUShaderCreateInfo;
|
||||
struct GPUStageInterfaceInfo;
|
||||
namespace blender {
|
||||
|
||||
#ifndef __cplusplus
|
||||
# include "../generic/py_capi_utils.hh"
|
||||
#endif
|
||||
|
||||
namespace gpu {
|
||||
class Shader;
|
||||
} // namespace gpu
|
||||
|
||||
/* Make sure that there is always a reference count for PyObjects of type String as the strings are
|
||||
* passed by reference in the #GPUStageInterfaceInfo and #GPUShaderCreateInfo APIs. */
|
||||
#define USE_GPU_PY_REFERENCES
|
||||
|
||||
/* `gpu_py_shader.cc` */
|
||||
|
||||
extern PyTypeObject BPyGPUShader_Type;
|
||||
|
||||
#define BPyGPUShader_Check(v) (Py_TYPE(v) == &BPyGPUShader_Type)
|
||||
|
||||
struct BPyGPUShader {
|
||||
PyObject_HEAD
|
||||
gpu::Shader *shader;
|
||||
bool is_builtin;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUShader_CreatePyObject(gpu::Shader *shader, bool is_builtin);
|
||||
[[nodiscard]] PyObject *bpygpu_shader_init();
|
||||
|
||||
/* gpu_py_shader_create_info.cc */
|
||||
|
||||
extern const struct PyC_StringEnumItems pygpu_attrtype_items[];
|
||||
extern PyTypeObject BPyGPUShaderCreateInfo_Type;
|
||||
extern PyTypeObject BPyGPUStageInterfaceInfo_Type;
|
||||
|
||||
#define BPyGPUShaderCreateInfo_Check(v) (Py_TYPE(v) == &BPyGPUShaderCreateInfo_Type)
|
||||
#define BPyGPUStageInterfaceInfo_Check(v) (Py_TYPE(v) == &BPyGPUStageInterfaceInfo_Type)
|
||||
|
||||
struct BPyGPUStageInterfaceInfo {
|
||||
PyObject_HEAD
|
||||
GPUStageInterfaceInfo *interface;
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Just to keep a user to prevent freeing buffers we're using. */
|
||||
PyObject *references;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct BPyGPUShaderCreateInfo {
|
||||
PyObject_HEAD
|
||||
GPUShaderCreateInfo *info;
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Just to keep a user to prevent freeing buffers we're using. */
|
||||
PyObject *vertex_source;
|
||||
PyObject *fragment_source;
|
||||
PyObject *compute_source;
|
||||
PyObject *typedef_source;
|
||||
PyObject *references;
|
||||
#endif
|
||||
size_t constants_total_size;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUStageInterfaceInfo_CreatePyObject(GPUStageInterfaceInfo *interface);
|
||||
[[nodiscard]] PyObject *BPyGPUShaderCreateInfo_CreatePyObject(GPUShaderCreateInfo *info);
|
||||
[[nodiscard]] bool bpygpu_shader_is_polyline(gpu::Shader *shader);
|
||||
|
||||
} // namespace blender
|
||||
1477
blender-5.2.0/source/blender/python/gpu/gpu_py_shader_create_info.cc
Normal file
1477
blender-5.2.0/source/blender/python/gpu/gpu_py_shader_create_info.cc
Normal file
File diff suppressed because it is too large
Load Diff
688
blender-5.2.0/source/blender/python/gpu/gpu_py_state.cc
Normal file
688
blender-5.2.0/source/blender/python/gpu/gpu_py_state.cc
Normal file
@@ -0,0 +1,688 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the gpu.state API.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_framebuffer.hh"
|
||||
#include "gpu_py_state.hh" /* own include */
|
||||
|
||||
/* Docstring Literal types. */
|
||||
#define PYDOC_BLEND_LITERAL \
|
||||
"Literal['NONE', 'ALPHA', 'ALPHA_PREMULT', 'ADDITIVE', " \
|
||||
"'ADDITIVE_PREMULT', 'MULTIPLY', 'SUBTRACT', 'INVERT']"
|
||||
#define PYDOC_DEPTHTEST_LITERAL \
|
||||
"Literal['NONE', 'ALWAYS', 'LESS', 'LESS_EQUAL', 'EQUAL', 'GREATER', 'GREATER_EQUAL']"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Helper Functions
|
||||
* \{ */
|
||||
|
||||
static const PyC_StringEnumItems pygpu_state_blend_items[] = {
|
||||
{GPU_BLEND_NONE, "NONE"},
|
||||
{GPU_BLEND_ALPHA, "ALPHA"},
|
||||
{GPU_BLEND_ALPHA_PREMULT, "ALPHA_PREMULT"},
|
||||
{GPU_BLEND_ADDITIVE, "ADDITIVE"},
|
||||
{GPU_BLEND_ADDITIVE_PREMULT, "ADDITIVE_PREMULT"},
|
||||
{GPU_BLEND_MULTIPLY, "MULTIPLY"},
|
||||
{GPU_BLEND_SUBTRACT, "SUBTRACT"},
|
||||
{GPU_BLEND_INVERT, "INVERT"},
|
||||
/**
|
||||
* These are quite special cases used inside the draw manager.
|
||||
* {GPU_BLEND_OIT, "OIT"},
|
||||
* {GPU_BLEND_BACKGROUND, "BACKGROUND"},
|
||||
* {GPU_BLEND_CUSTOM, "CUSTOM"},
|
||||
*/
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
static const PyC_StringEnumItems pygpu_state_depthtest_items[] = {
|
||||
{GPU_DEPTH_NONE, "NONE"},
|
||||
{GPU_DEPTH_ALWAYS, "ALWAYS"},
|
||||
{GPU_DEPTH_LESS, "LESS"},
|
||||
{GPU_DEPTH_LESS_EQUAL, "LESS_EQUAL"},
|
||||
{GPU_DEPTH_EQUAL, "EQUAL"},
|
||||
{GPU_DEPTH_GREATER, "GREATER"},
|
||||
{GPU_DEPTH_GREATER_EQUAL, "GREATER_EQUAL"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
static const PyC_StringEnumItems pygpu_state_faceculling_items[] = {
|
||||
{GPU_CULL_NONE, "NONE"},
|
||||
{GPU_CULL_FRONT, "FRONT"},
|
||||
{GPU_CULL_BACK, "BACK"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Manage Stack
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_blend_set_doc,
|
||||
".. function:: blend_set(mode)\n"
|
||||
"\n"
|
||||
" Defines the fixed pipeline blending equation.\n"
|
||||
"\n"
|
||||
" :param mode: The type of blend mode.\n"
|
||||
"\n"
|
||||
" * ``NONE`` No blending.\n"
|
||||
" * ``ALPHA`` The original color channels are interpolated according to the alpha "
|
||||
"value.\n"
|
||||
" * ``ALPHA_PREMULT`` The original color channels are interpolated according to the "
|
||||
"alpha value with the new colors pre-multiplied by this value.\n"
|
||||
" * ``ADDITIVE`` The original color channels are added by the corresponding ones.\n"
|
||||
" * ``ADDITIVE_PREMULT`` The original color channels are added by the corresponding ones "
|
||||
"that are pre-multiplied by the alpha value.\n"
|
||||
" * ``MULTIPLY`` The original color channels are multiplied by the corresponding ones.\n"
|
||||
" * ``SUBTRACT`` The original color channels are subtracted by the corresponding ones.\n"
|
||||
" * ``INVERT`` The original color channels are replaced by its complementary color.\n"
|
||||
//" * ``OIT``.\n"
|
||||
//" * ``BACKGROUND`` .\n"
|
||||
//" * ``CUSTOM`` .\n"
|
||||
" :type mode: " PYDOC_BLEND_LITERAL "\n");
|
||||
static PyObject *pygpu_state_blend_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
PyC_StringEnum pygpu_blend = {pygpu_state_blend_items};
|
||||
if (!PyC_ParseStringEnum(value, &pygpu_blend)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_blend(GPUBlend(pygpu_blend.value_found));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_blend_get_doc,
|
||||
".. function:: blend_get()\n"
|
||||
"\n"
|
||||
" Current blending equation.\n"
|
||||
"\n"
|
||||
" :return: The current blend mode.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_state_blend_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPUBlend blend = GPU_blend_get();
|
||||
return PyUnicode_FromString(PyC_StringEnum_FindIDFromValue(pygpu_state_blend_items, blend));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_clip_distances_set_doc,
|
||||
".. function:: clip_distances_set(distances_enabled)\n"
|
||||
"\n"
|
||||
" Sets the number of ``gl_ClipDistance`` planes used for clip geometry.\n"
|
||||
"\n"
|
||||
" :param distances_enabled: Number of clip distances enabled.\n"
|
||||
" :type distances_enabled: int\n");
|
||||
static PyObject *pygpu_state_clip_distances_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int distances_enabled = int(PyLong_AsUnsignedLong(value));
|
||||
if (distances_enabled == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (distances_enabled > 6) {
|
||||
PyErr_SetString(PyExc_ValueError, "too many distances enabled, max is 6");
|
||||
}
|
||||
|
||||
GPU_clip_distances(distances_enabled);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_depth_test_set_doc,
|
||||
".. function:: depth_test_set(mode)\n"
|
||||
"\n"
|
||||
" Defines the depth_test equation.\n"
|
||||
"\n"
|
||||
" :param mode: The depth test equation name.\n"
|
||||
" :type mode: " PYDOC_DEPTHTEST_LITERAL "\n");
|
||||
static PyObject *pygpu_state_depth_test_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
PyC_StringEnum pygpu_depth_test = {pygpu_state_depthtest_items};
|
||||
if (!PyC_ParseStringEnum(value, &pygpu_depth_test)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_depth_test(GPUDepthTest(pygpu_depth_test.value_found));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_depth_test_get_doc,
|
||||
".. function:: depth_test_get()\n"
|
||||
"\n"
|
||||
" Current depth_test equation.\n"
|
||||
"\n"
|
||||
" :return: The current depth test mode.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_state_depth_test_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPUDepthTest test = GPU_depth_test_get();
|
||||
return PyUnicode_FromString(PyC_StringEnum_FindIDFromValue(pygpu_state_depthtest_items, test));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_depth_mask_set_doc,
|
||||
".. function:: depth_mask_set(value)\n"
|
||||
"\n"
|
||||
" Write to depth component.\n"
|
||||
"\n"
|
||||
" :param value: True for writing to the depth component.\n"
|
||||
" :type value: bool\n");
|
||||
static PyObject *pygpu_state_depth_mask_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
bool write_to_depth;
|
||||
if (!PyC_ParseBool(value, &write_to_depth)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_depth_mask(write_to_depth);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_depth_mask_get_doc,
|
||||
".. function:: depth_mask_get()\n"
|
||||
"\n"
|
||||
" Writing status in the depth component.\n"
|
||||
"\n"
|
||||
" :return: True if writing to the depth component is enabled.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *pygpu_state_depth_mask_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyBool_FromLong(GPU_depth_mask_get());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_viewport_set_doc,
|
||||
".. function:: viewport_set(x, y, xsize, ysize)\n"
|
||||
"\n"
|
||||
" Specifies the viewport of the active framebuffer.\n"
|
||||
" Note: The viewport state is not saved upon framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x coordinate, in pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y coordinate, in pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the viewport.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the viewport.\n"
|
||||
" :type ysize: int\n");
|
||||
static PyObject *pygpu_state_viewport_set(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int x, y, xsize, ysize;
|
||||
if (!PyArg_ParseTuple(args, "iiii:viewport_set", &x, &y, &xsize, &ysize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_viewport(x, y, xsize, ysize);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_viewport_get_doc,
|
||||
".. function:: viewport_get()\n"
|
||||
"\n"
|
||||
" Viewport of the active framebuffer.\n"
|
||||
"\n"
|
||||
" :return: The viewport as a tuple (x, y, xsize, ysize).\n"
|
||||
" :rtype: tuple[int, int, int, int]\n");
|
||||
static PyObject *pygpu_state_viewport_get(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int viewport[4];
|
||||
GPU_viewport_size_get_i(viewport);
|
||||
|
||||
PyObject *ret = PyTuple_New(4);
|
||||
PyTuple_SET_ITEMS(ret,
|
||||
PyLong_FromLong(viewport[0]),
|
||||
PyLong_FromLong(viewport[1]),
|
||||
PyLong_FromLong(viewport[2]),
|
||||
PyLong_FromLong(viewport[3]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_scissor_set_doc,
|
||||
".. function:: scissor_set(x, y, xsize, ysize)\n"
|
||||
"\n"
|
||||
" Specifies the scissor area of the active framebuffer.\n"
|
||||
" Note: The scissor state is not saved upon framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x coordinate, in pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y coordinate, in pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the scissor rectangle.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the scissor rectangle.\n"
|
||||
" :type ysize: int\n");
|
||||
static PyObject *pygpu_state_scissor_set(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int x, y, xsize, ysize;
|
||||
if (!PyArg_ParseTuple(args, "iiii:scissor_set", &x, &y, &xsize, &ysize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_scissor(x, y, xsize, ysize);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_scissor_get_doc,
|
||||
".. function:: scissor_get()\n"
|
||||
"\n"
|
||||
" Retrieve the scissors of the active framebuffer.\n"
|
||||
" Note: Only valid between 'scissor_set' and a framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :return: The scissor of the active framebuffer as a tuple\n"
|
||||
" (x, y, xsize, ysize).\n"
|
||||
" x, y: lower left corner of the scissor rectangle, in pixels.\n"
|
||||
" xsize, ysize: width and height of the scissor rectangle.\n"
|
||||
" :rtype: tuple[int, int, int, int]\n");
|
||||
static PyObject *pygpu_state_scissor_get(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int scissor[4];
|
||||
GPU_scissor_get(scissor);
|
||||
|
||||
PyObject *ret = PyTuple_New(4);
|
||||
PyTuple_SET_ITEMS(ret,
|
||||
PyLong_FromLong(scissor[0]),
|
||||
PyLong_FromLong(scissor[1]),
|
||||
PyLong_FromLong(scissor[2]),
|
||||
PyLong_FromLong(scissor[3]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_scissor_test_set_doc,
|
||||
".. function:: scissor_test_set(enable)\n"
|
||||
"\n"
|
||||
" Enable/disable scissor testing on the active framebuffer.\n"
|
||||
"\n"
|
||||
" :param enable:\n"
|
||||
" True - enable scissor testing.\n"
|
||||
" False - disable scissor testing.\n"
|
||||
" :type enable: bool\n");
|
||||
static PyObject *pygpu_state_scissor_test_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
bool enabled;
|
||||
if (!PyC_ParseBool(value, &enabled)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_scissor_test(enabled);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_line_width_set_doc,
|
||||
".. function:: line_width_set(width)\n"
|
||||
"\n"
|
||||
" Specify the width of rasterized lines.\n"
|
||||
"\n"
|
||||
" :param width: New width.\n"
|
||||
" :type width: float\n");
|
||||
static PyObject *pygpu_state_line_width_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float width = float(PyFloat_AsDouble(value));
|
||||
if (PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_line_width(width);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_line_width_get_doc,
|
||||
".. function:: line_width_get()\n"
|
||||
"\n"
|
||||
" Current width of rasterized lines.\n"
|
||||
"\n"
|
||||
" :return: The current line width.\n"
|
||||
" :rtype: float\n");
|
||||
static PyObject *pygpu_state_line_width_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float width = GPU_line_width_get();
|
||||
return PyFloat_FromDouble(double(width));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_point_size_set_doc,
|
||||
".. function:: point_size_set(size)\n"
|
||||
"\n"
|
||||
" Specify the diameter of rasterized points.\n"
|
||||
"\n"
|
||||
" When ``program_point_size_set(False)`` is set, this sets the ``size``\n"
|
||||
" uniform of point shaders. Any value set by ``uniform_float(\"size\", ...)``\n"
|
||||
" on a point shader will be overwritten by this function. Use\n"
|
||||
" ``program_point_size_set(True)`` to disable this override and control\n"
|
||||
" point size via ``gl_PointSize`` in the vertex shader.\n"
|
||||
"\n"
|
||||
" :param size: New diameter.\n"
|
||||
" :type size: float\n");
|
||||
static PyObject *pygpu_state_point_size_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float size = float(PyFloat_AsDouble(value));
|
||||
if (PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_point_size(size);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_color_mask_set_doc,
|
||||
".. function:: color_mask_set(r, g, b, a)\n"
|
||||
"\n"
|
||||
" Enable or disable writing of frame buffer color components.\n"
|
||||
"\n"
|
||||
" :param r: Red component.\n"
|
||||
" :type r: bool\n"
|
||||
" :param g: Green component.\n"
|
||||
" :type g: bool\n"
|
||||
" :param b: Blue component.\n"
|
||||
" :type b: bool\n"
|
||||
" :param a: Alpha component.\n"
|
||||
" :type a: bool\n");
|
||||
static PyObject *pygpu_state_color_mask_set(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int r, g, b, a;
|
||||
if (!PyArg_ParseTuple(args, "pppp:color_mask_set", &r, &g, &b, &a)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_color_mask(bool(r), bool(g), bool(b), bool(a));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_face_culling_set_doc,
|
||||
".. function:: face_culling_set(culling)\n"
|
||||
"\n"
|
||||
" Specify whether none, front-facing or back-facing facets can be culled.\n"
|
||||
"\n"
|
||||
" :param culling: The face culling mode.\n"
|
||||
" :type culling: Literal['NONE', 'FRONT', 'BACK']\n");
|
||||
static PyObject *pygpu_state_face_culling_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
PyC_StringEnum pygpu_faceculling = {pygpu_state_faceculling_items};
|
||||
if (!PyC_ParseStringEnum(value, &pygpu_faceculling)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_face_culling(GPUFaceCullTest(pygpu_faceculling.value_found));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_front_facing_set_doc,
|
||||
".. function:: front_facing_set(invert)\n"
|
||||
"\n"
|
||||
" Specifies the orientation of front-facing polygons.\n"
|
||||
"\n"
|
||||
" :param invert: True for clockwise polygons as front-facing.\n"
|
||||
" :type invert: bool\n");
|
||||
static PyObject *pygpu_state_front_facing_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
bool invert;
|
||||
if (!PyC_ParseBool(value, &invert)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_front_facing(invert);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_program_point_size_set_doc,
|
||||
".. function:: program_point_size_set(enable)\n"
|
||||
"\n"
|
||||
" When enabled (True), point size is taken from the shader builtin\n"
|
||||
" ``gl_PointSize``. When disabled (False), the global state value can\n"
|
||||
" be applied to the shader ``size`` uniform, overwriting any user-set\n"
|
||||
" value.\n"
|
||||
"\n"
|
||||
" :param enable: True to use shader ``gl_PointSize``, False for global state.\n"
|
||||
" :type enable: bool\n");
|
||||
static PyObject *pygpu_state_program_point_size_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
bool enable;
|
||||
if (!PyC_ParseBool(value, &enable)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_program_point_size(enable);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_active_framebuffer_get_doc,
|
||||
".. function:: active_framebuffer_get()\n"
|
||||
"\n"
|
||||
" Return the active frame-buffer in context.\n"
|
||||
"\n"
|
||||
" :return: The active framebuffer.\n"
|
||||
" :rtype: :class:`gpu.types.GPUFrameBuffer`\n");
|
||||
static PyObject *pygpu_state_active_framebuffer_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
gpu::FrameBuffer *fb = GPU_framebuffer_active_get();
|
||||
return BPyGPUFrameBuffer_CreatePyObject(fb, true);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_state__tp_methods[] = {
|
||||
/* Manage Stack */
|
||||
{"blend_set",
|
||||
static_cast<PyCFunction>(pygpu_state_blend_set),
|
||||
METH_O,
|
||||
pygpu_state_blend_set_doc},
|
||||
{"blend_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_blend_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_blend_get_doc},
|
||||
{"clip_distances_set",
|
||||
static_cast<PyCFunction>(pygpu_state_clip_distances_set),
|
||||
METH_O,
|
||||
pygpu_state_clip_distances_set_doc},
|
||||
{"depth_test_set",
|
||||
static_cast<PyCFunction>(pygpu_state_depth_test_set),
|
||||
METH_O,
|
||||
pygpu_state_depth_test_set_doc},
|
||||
{"depth_test_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_depth_test_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_depth_test_get_doc},
|
||||
{"depth_mask_set",
|
||||
static_cast<PyCFunction>(pygpu_state_depth_mask_set),
|
||||
METH_O,
|
||||
pygpu_state_depth_mask_set_doc},
|
||||
{"depth_mask_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_depth_mask_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_depth_mask_get_doc},
|
||||
{"viewport_set",
|
||||
static_cast<PyCFunction>(pygpu_state_viewport_set),
|
||||
METH_VARARGS,
|
||||
pygpu_state_viewport_set_doc},
|
||||
{"viewport_get",
|
||||
static_cast<PyCFunction>(pygpu_state_viewport_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_viewport_get_doc},
|
||||
{"scissor_set",
|
||||
static_cast<PyCFunction>(pygpu_state_scissor_set),
|
||||
METH_VARARGS,
|
||||
pygpu_state_scissor_set_doc},
|
||||
{"scissor_get",
|
||||
static_cast<PyCFunction>(pygpu_state_scissor_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_scissor_get_doc},
|
||||
{"scissor_test_set",
|
||||
static_cast<PyCFunction>(pygpu_state_scissor_test_set),
|
||||
METH_O,
|
||||
pygpu_state_scissor_test_set_doc},
|
||||
{"line_width_set",
|
||||
static_cast<PyCFunction>(pygpu_state_line_width_set),
|
||||
METH_O,
|
||||
pygpu_state_line_width_set_doc},
|
||||
{"line_width_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_line_width_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_line_width_get_doc},
|
||||
{"point_size_set",
|
||||
static_cast<PyCFunction>(pygpu_state_point_size_set),
|
||||
METH_O,
|
||||
pygpu_state_point_size_set_doc},
|
||||
{"color_mask_set",
|
||||
static_cast<PyCFunction>(pygpu_state_color_mask_set),
|
||||
METH_VARARGS,
|
||||
pygpu_state_color_mask_set_doc},
|
||||
{"face_culling_set",
|
||||
static_cast<PyCFunction>(pygpu_state_face_culling_set),
|
||||
METH_O,
|
||||
pygpu_state_face_culling_set_doc},
|
||||
{"front_facing_set",
|
||||
static_cast<PyCFunction>(pygpu_state_front_facing_set),
|
||||
METH_O,
|
||||
pygpu_state_front_facing_set_doc},
|
||||
{"program_point_size_set",
|
||||
static_cast<PyCFunction>(pygpu_state_program_point_size_set),
|
||||
METH_O,
|
||||
pygpu_state_program_point_size_set_doc},
|
||||
{"active_framebuffer_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_active_framebuffer_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_active_framebuffer_get_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state__tp_doc,
|
||||
"This module provides access to the gpu state.");
|
||||
static PyModuleDef pygpu_state_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.state",
|
||||
/*m_doc*/ pygpu_state__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_state__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_state_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_state_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/gpu/gpu_py_state.hh
Normal file
17
blender-5.2.0/source/blender/python/gpu/gpu_py_state.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_state_init();
|
||||
|
||||
} // namespace blender
|
||||
1022
blender-5.2.0/source/blender/python/gpu/gpu_py_texture.cc
Normal file
1022
blender-5.2.0/source/blender/python/gpu/gpu_py_texture.cc
Normal file
File diff suppressed because it is too large
Load Diff
44
blender-5.2.0/source/blender/python/gpu/gpu_py_texture.hh
Normal file
44
blender-5.2.0/source/blender/python/gpu/gpu_py_texture.hh
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
}
|
||||
|
||||
extern PyTypeObject BPyGPUTexture_Type;
|
||||
|
||||
/**
|
||||
* GPU_DEPTH24_STENCIL8 and GPU_DEPTH_COMPONENT24 are deprecated in Blender 5.0. These formats are
|
||||
* automatically converted to their 32F variant.
|
||||
*/
|
||||
constexpr int GPU_DEPTH24_STENCIL8_DEPRECATED = -1;
|
||||
constexpr int GPU_DEPTH_COMPONENT24_DEPRECATED = -2;
|
||||
extern const struct PyC_StringEnumItems pygpu_textureformat_items[];
|
||||
|
||||
#define BPyGPUTexture_Check(v) (Py_TYPE(v) == &BPyGPUTexture_Type)
|
||||
|
||||
struct BPyGPUTexture {
|
||||
PyObject_HEAD
|
||||
gpu::Texture *tex;
|
||||
};
|
||||
|
||||
[[nodiscard]] int bpygpu_ParseTexture(PyObject *o, void *p);
|
||||
[[nodiscard]] PyObject *bpygpu_texture_init();
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUTexture_CreatePyObject(gpu::Texture *tex, bool shared_reference)
|
||||
ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
109
blender-5.2.0/source/blender/python/gpu/gpu_py_types.cc
Normal file
109
blender-5.2.0/source/blender/python/gpu/gpu_py_types.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "gpu_py_matrix.hh"
|
||||
#include "gpu_py_offscreen.hh"
|
||||
#include "gpu_py_types.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Types Module
|
||||
* \{ */
|
||||
|
||||
static PyModuleDef pygpu_types_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.types",
|
||||
/*m_doc*/ nullptr,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ nullptr,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_types_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_types_module_def);
|
||||
|
||||
if (PyType_Ready(&BPyGPU_BufferType) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUVertFormat_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUVertBuf_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUIndexBuf_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUBatch_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUOffScreen_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUShader_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUTexture_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUFrameBuffer_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUUniformBuf_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUShaderCreateInfo_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUStageInterfaceInfo_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&PyGPUMatrixStackContext_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&PyGPUOffscreenStackContext_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPU_DeviceType) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyModule_AddType(submodule, &BPyGPU_BufferType);
|
||||
PyModule_AddType(submodule, &BPyGPUVertFormat_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUVertBuf_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUIndexBuf_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUBatch_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUOffScreen_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUShader_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUTexture_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUFrameBuffer_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUUniformBuf_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUShaderCreateInfo_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUStageInterfaceInfo_Type);
|
||||
PyModule_AddType(submodule, &PyGPUMatrixStackContext_Type);
|
||||
PyModule_AddType(submodule, &PyGPUOffscreenStackContext_Type);
|
||||
PyModule_AddType(submodule, &BPyGPU_DeviceType);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
29
blender-5.2.0/source/blender/python/gpu/gpu_py_types.hh
Normal file
29
blender-5.2.0/source/blender/python/gpu/gpu_py_types.hh
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_py_buffer.hh" // IWYU pragma: export
|
||||
|
||||
#include "gpu_py_batch.hh" // IWYU pragma: export
|
||||
#include "gpu_py_compute.hh" // IWYU pragma: export
|
||||
#include "gpu_py_element.hh" // IWYU pragma: export
|
||||
#include "gpu_py_framebuffer.hh" // IWYU pragma: export
|
||||
#include "gpu_py_offscreen.hh" // IWYU pragma: export
|
||||
#include "gpu_py_platform.hh" // IWYU pragma: export
|
||||
#include "gpu_py_shader.hh" // IWYU pragma: export
|
||||
#include "gpu_py_texture.hh" // IWYU pragma: export
|
||||
#include "gpu_py_uniformbuffer.hh" // IWYU pragma: export
|
||||
#include "gpu_py_vertex_buffer.hh" // IWYU pragma: export
|
||||
#include "gpu_py_vertex_format.hh" // IWYU pragma: export
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_types_init();
|
||||
|
||||
} // namespace blender
|
||||
257
blender-5.2.0/source/blender/python/gpu/gpu_py_uniformbuffer.cc
Normal file
257
blender-5.2.0/source/blender/python/gpu/gpu_py_uniformbuffer.cc
Normal file
@@ -0,0 +1,257 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the uniform buffer functionalities of the 'gpu' module
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_uniform_buffer.hh"
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_uniformbuffer.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name gpu::UniformBuf Common Utilities
|
||||
* \{ */
|
||||
|
||||
static int pygpu_uniformbuffer_valid_check(BPyGPUUniformBuf *bpygpu_ub)
|
||||
{
|
||||
if (UNLIKELY(bpygpu_ub->ubo == nullptr)) {
|
||||
PyErr_SetString(PyExc_ReferenceError,
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
"GPU uniform buffer was freed, no further access is valid");
|
||||
#else
|
||||
|
||||
"GPU uniform buffer: internal error");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BPYGPU_UNIFORMBUF_CHECK_OBJ(bpygpu) \
|
||||
{ \
|
||||
if (UNLIKELY(pygpu_uniformbuffer_valid_check(bpygpu) == -1)) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name gpu::UniformBuf Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_uniformbuffer__tp_new(PyTypeObject * /*self*/,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
gpu::UniformBuf *ubo = nullptr;
|
||||
PyObject *pybuffer_obj;
|
||||
char err_out[256] = "unknown error. See console";
|
||||
|
||||
static const char *_keywords[] = {"data", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O" /* `data` */
|
||||
":GPUUniformBuf.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &pybuffer_obj)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!GPU_context_active_get()) {
|
||||
STRNCPY_UTF8(err_out, "No active GPU context found");
|
||||
}
|
||||
else {
|
||||
Py_buffer pybuffer;
|
||||
if (PyObject_GetBuffer(pybuffer_obj, &pybuffer, PyBUF_SIMPLE) == -1) {
|
||||
/* PyObject_GetBuffer raise a PyExc_BufferError */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((pybuffer.len % 16) != 0) {
|
||||
STRNCPY_UTF8(err_out, "UBO is not padded to size of vec4");
|
||||
}
|
||||
else {
|
||||
ubo = GPU_uniformbuf_create_ex(pybuffer.len, pybuffer.buf, "python_uniformbuffer");
|
||||
}
|
||||
PyBuffer_Release(&pybuffer);
|
||||
}
|
||||
|
||||
if (ubo == nullptr) {
|
||||
PyErr_Format(PyExc_RuntimeError, "GPUUniformBuf.__new__(...) failed with '%s'", err_out);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return BPyGPUUniformBuf_CreatePyObject(ubo);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_uniformbuffer_update_doc,
|
||||
".. method:: update(data)\n"
|
||||
"\n"
|
||||
" Update the data of the uniform buffer object.\n"
|
||||
"\n"
|
||||
" :param data: Data to fill the buffer.\n"
|
||||
" :type data: Buffer\n");
|
||||
static PyObject *pygpu_uniformbuffer_update(BPyGPUUniformBuf *self, PyObject *obj)
|
||||
{
|
||||
BPYGPU_UNIFORMBUF_CHECK_OBJ(self);
|
||||
|
||||
Py_buffer pybuffer;
|
||||
if (PyObject_GetBuffer(obj, &pybuffer, PyBUF_SIMPLE) == -1) {
|
||||
/* PyObject_GetBuffer raise a PyExc_BufferError */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_uniformbuf_update(self->ubo, pybuffer.buf);
|
||||
PyBuffer_Release(&pybuffer);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_uniformbuffer_free_doc,
|
||||
".. method:: free()\n"
|
||||
"\n"
|
||||
" Free the uniform buffer object.\n"
|
||||
" The uniform buffer object will no longer be accessible.\n");
|
||||
static PyObject *pygpu_uniformbuffer_free(BPyGPUUniformBuf *self)
|
||||
{
|
||||
BPYGPU_UNIFORMBUF_CHECK_OBJ(self);
|
||||
|
||||
GPU_uniformbuf_free(self->ubo);
|
||||
self->ubo = nullptr;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void BPyGPUUniformBuf__tp_dealloc(BPyGPUUniformBuf *self)
|
||||
{
|
||||
if (self->ubo) {
|
||||
GPU_uniformbuf_free(self->ubo);
|
||||
}
|
||||
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject *>(self));
|
||||
}
|
||||
|
||||
static PyGetSetDef pygpu_uniformbuffer__tp_getseters[] = {
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMethodDef pygpu_uniformbuffer__tp_methods[] = {
|
||||
{"update",
|
||||
reinterpret_cast<PyCFunction>(pygpu_uniformbuffer_update),
|
||||
METH_O,
|
||||
pygpu_uniformbuffer_update_doc},
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
{"free", (PyCFunction)pygpu_uniformbuffer_free, METH_NOARGS, pygpu_uniformbuffer_free_doc},
|
||||
#endif
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_uniformbuffer__tp_doc,
|
||||
".. class:: GPUUniformBuf\n"
|
||||
"\n"
|
||||
" This object gives access to uniform buffers.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(data)\n"
|
||||
"\n"
|
||||
" :param data: Data to fill the buffer.\n"
|
||||
" :type data: Buffer\n");
|
||||
PyTypeObject BPyGPUUniformBuf_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUUniformBuf",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUUniformBuf),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(BPyGPUUniformBuf__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_uniformbuffer__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_uniformbuffer__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_uniformbuffer__tp_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_uniformbuffer__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUUniformBuf_CreatePyObject(gpu::UniformBuf *ubo)
|
||||
{
|
||||
BPyGPUUniformBuf *self;
|
||||
|
||||
self = PyObject_New(BPyGPUUniformBuf, &BPyGPUUniformBuf_Type);
|
||||
self->ubo = ubo;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef BPYGPU_UNIFORMBUF_CHECK_OBJ
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class UniformBuf;
|
||||
} // namespace gpu
|
||||
|
||||
extern PyTypeObject BPyGPUUniformBuf_Type;
|
||||
|
||||
#define BPyGPUUniformBuf_Check(v) (Py_TYPE(v) == &BPyGPUUniformBuf_Type)
|
||||
|
||||
struct BPyGPUUniformBuf {
|
||||
PyObject_HEAD
|
||||
gpu::UniformBuf *ubo;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUUniformBuf_CreatePyObject(gpu::UniformBuf *ubo) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
434
blender-5.2.0/source/blender/python/gpu/gpu_py_vertex_buffer.cc
Normal file
434
blender-5.2.0/source/blender/python/gpu/gpu_py_vertex_buffer.cc
Normal file
@@ -0,0 +1,434 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_vertex_buffer.hh" /* own include */
|
||||
#include "gpu_py_vertex_format.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility Functions
|
||||
* \{ */
|
||||
|
||||
#define PYGPU_AS_NATIVE_SWITCH(attr) \
|
||||
switch (attr->type.comp_type()) { \
|
||||
case GPU_COMP_I8: { \
|
||||
PY_AS_NATIVE(int8_t, PyC_Long_AsI8); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_U8: { \
|
||||
PY_AS_NATIVE(uint8_t, PyC_Long_AsU8); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_I16: { \
|
||||
PY_AS_NATIVE(int16_t, PyC_Long_AsI16); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_U16: { \
|
||||
PY_AS_NATIVE(uint16_t, PyC_Long_AsU16); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_I32: { \
|
||||
PY_AS_NATIVE(int32_t, PyC_Long_AsI32); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_U32: { \
|
||||
PY_AS_NATIVE(uint32_t, PyC_Long_AsU32); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_F32: { \
|
||||
PY_AS_NATIVE(float, PyFloat_AsDouble); \
|
||||
break; \
|
||||
} \
|
||||
default: \
|
||||
BLI_assert_unreachable(); \
|
||||
break; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* No error checking, callers must run PyErr_Occurred */
|
||||
static void pygpu_fill_format_elem(void *data_dst_void, PyObject *py_src, const GPUVertAttr *attr)
|
||||
{
|
||||
#define PY_AS_NATIVE(ty_dst, py_as_native) \
|
||||
{ \
|
||||
ty_dst *data_dst = static_cast<ty_dst *>(data_dst_void); \
|
||||
*data_dst = py_as_native(py_src); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
PYGPU_AS_NATIVE_SWITCH(attr);
|
||||
|
||||
#undef PY_AS_NATIVE
|
||||
}
|
||||
|
||||
/* No error checking, callers must run PyErr_Occurred */
|
||||
static void pygpu_fill_format_sequence(void *data_dst_void,
|
||||
PyObject *py_seq_fast,
|
||||
const GPUVertAttr *attr)
|
||||
{
|
||||
const uint len = attr->type.comp_len();
|
||||
PyObject **value_fast_items = PySequence_Fast_ITEMS(py_seq_fast);
|
||||
|
||||
/**
|
||||
* Args are constants, so range checks will be optimized out if they're no-op's.
|
||||
*/
|
||||
#define PY_AS_NATIVE(ty_dst, py_as_native) \
|
||||
ty_dst *data_dst = static_cast<ty_dst *>(data_dst_void); \
|
||||
for (uint i = 0; i < len; i++) { \
|
||||
data_dst[i] = py_as_native(value_fast_items[i]); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
PYGPU_AS_NATIVE_SWITCH(attr);
|
||||
|
||||
#undef PY_AS_NATIVE
|
||||
}
|
||||
|
||||
#undef PYGPU_AS_NATIVE_SWITCH
|
||||
#undef WARN_TYPE_LIMIT_PUSH
|
||||
#undef WARN_TYPE_LIMIT_POP
|
||||
|
||||
static bool pygpu_vertbuf_fill_impl(gpu::VertBuf *vbo,
|
||||
uint data_id,
|
||||
PyObject *seq,
|
||||
const char *error_prefix)
|
||||
{
|
||||
const char *exc_str_size_mismatch = "Expected a %s of size %d, got %u";
|
||||
|
||||
bool ok = true;
|
||||
const GPUVertAttr *attr = &GPU_vertbuf_get_format(vbo)->attrs[data_id];
|
||||
uint vert_len = GPU_vertbuf_get_vertex_len(vbo);
|
||||
|
||||
if (PyObject_CheckBuffer(seq)) {
|
||||
Py_buffer pybuffer;
|
||||
|
||||
if (PyObject_GetBuffer(seq, &pybuffer, PyBUF_STRIDES | PyBUF_ND) == -1) {
|
||||
/* PyObject_GetBuffer raise a PyExc_BufferError */
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint comp_len = pybuffer.ndim == 1 ? 1 : uint(pybuffer.shape[1]);
|
||||
|
||||
if (pybuffer.shape[0] != vert_len) {
|
||||
PyErr_Format(
|
||||
PyExc_ValueError, exc_str_size_mismatch, "sequence", vert_len, pybuffer.shape[0]);
|
||||
ok = false;
|
||||
}
|
||||
else if (comp_len != attr->type.comp_len()) {
|
||||
PyErr_Format(
|
||||
PyExc_ValueError, exc_str_size_mismatch, "component", attr->type.comp_len(), comp_len);
|
||||
ok = false;
|
||||
}
|
||||
else {
|
||||
GPU_vertbuf_attr_fill_stride(vbo, data_id, pybuffer.strides[0], pybuffer.buf);
|
||||
}
|
||||
|
||||
PyBuffer_Release(&pybuffer);
|
||||
}
|
||||
else {
|
||||
GPUVertBufRaw data_step;
|
||||
GPU_vertbuf_attr_get_raw_data(vbo, data_id, &data_step);
|
||||
|
||||
PyObject *seq_fast = PySequence_Fast(seq, "Vertex buffer fill");
|
||||
if (seq_fast == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint seq_len = PySequence_Fast_GET_SIZE(seq_fast);
|
||||
|
||||
if (seq_len != vert_len) {
|
||||
PyErr_Format(PyExc_ValueError, exc_str_size_mismatch, "sequence", vert_len, seq_len);
|
||||
}
|
||||
|
||||
PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
|
||||
|
||||
if (attr->type.comp_len() == 1) {
|
||||
for (uint i = 0; i < seq_len; i++) {
|
||||
uchar *data = static_cast<uchar *>(GPU_vertbuf_raw_step(&data_step));
|
||||
PyObject *item = seq_items[i];
|
||||
pygpu_fill_format_elem(data, item, attr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (uint i = 0; i < seq_len; i++) {
|
||||
uchar *data = static_cast<uchar *>(GPU_vertbuf_raw_step(&data_step));
|
||||
PyObject *seq_fast_item = PySequence_Fast(seq_items[i], error_prefix);
|
||||
|
||||
if (seq_fast_item == nullptr) {
|
||||
ok = false;
|
||||
goto finally;
|
||||
}
|
||||
if (PySequence_Fast_GET_SIZE(seq_fast_item) != attr->type.comp_len()) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
exc_str_size_mismatch,
|
||||
"sequence",
|
||||
attr->type.comp_len(),
|
||||
PySequence_Fast_GET_SIZE(seq_fast_item));
|
||||
ok = false;
|
||||
Py_DECREF(seq_fast_item);
|
||||
goto finally;
|
||||
}
|
||||
|
||||
/* May trigger error, check below */
|
||||
pygpu_fill_format_sequence(data, seq_fast_item, attr);
|
||||
Py_DECREF(seq_fast_item);
|
||||
}
|
||||
}
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
finally:
|
||||
|
||||
Py_DECREF(seq_fast);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int pygpu_vertbuf_fill(gpu::VertBuf *buf,
|
||||
int id,
|
||||
PyObject *py_seq_data,
|
||||
const char *error_prefix)
|
||||
{
|
||||
if (id < 0 || id >= GPU_vertbuf_get_format(buf)->attr_len) {
|
||||
PyErr_Format(PyExc_ValueError, "Format id %d out of range", id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buf->data<char>().data() == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError, "Can't fill, static buffer already in use");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pygpu_vertbuf_fill_impl(buf, uint(id), py_seq_data, error_prefix)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VertBuf Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_vertbuf__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
struct {
|
||||
PyObject *py_fmt;
|
||||
uint len;
|
||||
} params;
|
||||
|
||||
static const char *_keywords[] = {"format", "len", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O!" /* `format` */
|
||||
"I" /* `len` */
|
||||
":GPUVertBuf.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &BPyGPUVertFormat_Type, ¶ms.py_fmt, ¶ms.len))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const GPUVertFormat &fmt = (reinterpret_cast<BPyGPUVertFormat *>(params.py_fmt))->fmt;
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(fmt);
|
||||
|
||||
GPU_vertbuf_data_alloc(*vbo, params.len);
|
||||
|
||||
return BPyGPUVertBuf_CreatePyObject(vbo);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertbuf_attr_fill_doc,
|
||||
".. method:: attr_fill(id, data)\n"
|
||||
"\n"
|
||||
" Insert data into the buffer for a single attribute.\n"
|
||||
"\n"
|
||||
" :param id: Either the name or the id of the attribute.\n"
|
||||
" :type id: int | str\n"
|
||||
" :param data: Buffer or sequence of data that should be stored in the buffer\n"
|
||||
" :type data: Buffer | "
|
||||
"Sequence[float] | Sequence[int] | Sequence[Sequence[float]] | Sequence[Sequence[int]]\n");
|
||||
static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *data;
|
||||
PyObject *identifier;
|
||||
|
||||
static const char *_keywords[] = {"id", "data", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O" /* `id` */
|
||||
"O" /* `data` */
|
||||
":attr_fill",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &identifier, &data)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int id;
|
||||
|
||||
if (PyLong_Check(identifier)) {
|
||||
id = PyLong_AsLong(identifier);
|
||||
}
|
||||
else if (PyUnicode_Check(identifier)) {
|
||||
const GPUVertFormat *format = GPU_vertbuf_get_format(self->buf);
|
||||
const char *name = PyUnicode_AsUTF8(identifier);
|
||||
id = GPU_vertformat_attr_id_get(format, name);
|
||||
if (id == -1) {
|
||||
PyErr_Format(PyExc_ValueError, "Unknown attribute '%s'", name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError, "expected int or str type as identifier");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!pygpu_vertbuf_fill(self->buf, id, data, "GPUVertBuf.attr_fill")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_vertbuf__tp_methods[] = {
|
||||
{"attr_fill",
|
||||
reinterpret_cast<PyCFunction>(pygpu_vertbuf_attr_fill),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_vertbuf_attr_fill_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self)
|
||||
{
|
||||
GPU_vertbuf_discard(self->buf);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertbuf__tp_doc,
|
||||
".. class:: GPUVertBuf\n"
|
||||
"\n"
|
||||
" Contains a VBO.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(format, len)\n"
|
||||
"\n"
|
||||
" :param format: Vertex format.\n"
|
||||
" :type format: :class:`gpu.types.GPUVertFormat`\n"
|
||||
" :param len: Amount of vertices that will fit into this buffer.\n"
|
||||
" :type len: int\n");
|
||||
PyTypeObject BPyGPUVertBuf_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUVertBuf",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUVertBuf),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_vertbuf__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_vertbuf__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_vertbuf__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_vertbuf__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUVertBuf_CreatePyObject(gpu::VertBuf *buf)
|
||||
{
|
||||
BPyGPUVertBuf *self;
|
||||
|
||||
self = PyObject_New(BPyGPUVertBuf, &BPyGPUVertBuf_Type);
|
||||
self->buf = buf;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class VertBuf;
|
||||
}
|
||||
|
||||
extern PyTypeObject BPyGPUVertBuf_Type;
|
||||
|
||||
#define BPyGPUVertBuf_Check(v) (Py_TYPE(v) == &BPyGPUVertBuf_Type)
|
||||
|
||||
struct BPyGPUVertBuf {
|
||||
PyObject_HEAD
|
||||
/* The buf is owned, we may support thin wrapped batches later. */
|
||||
gpu::VertBuf *buf;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUVertBuf_CreatePyObject(gpu::VertBuf *buf) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
265
blender-5.2.0/source/blender/python/gpu/gpu_py_vertex_format.cc
Normal file
265
blender-5.2.0/source/blender/python/gpu/gpu_py_vertex_format.cc
Normal file
@@ -0,0 +1,265 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_vertex_format.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Enum Conversion
|
||||
*
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
* \{ */
|
||||
|
||||
static PyC_StringEnumItems pygpu_vertcomptype_items[] = {
|
||||
{GPU_COMP_I8, "I8"},
|
||||
{GPU_COMP_U8, "U8"},
|
||||
{GPU_COMP_I16, "I16"},
|
||||
{GPU_COMP_U16, "U16"},
|
||||
{GPU_COMP_I32, "I32"},
|
||||
{GPU_COMP_U32, "U32"},
|
||||
{GPU_COMP_F32, "F32"},
|
||||
{GPU_COMP_I10, "I10"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
static PyC_StringEnumItems pygpu_vertfetchmode_items[] = {
|
||||
{GPU_FETCH_FLOAT, "FLOAT"},
|
||||
{GPU_FETCH_INT, "INT"},
|
||||
{GPU_FETCH_INT_TO_FLOAT_UNIT, "INT_TO_FLOAT_UNIT"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VertFormat Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_vertformat__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) {
|
||||
PyErr_SetString(PyExc_ValueError, "This function takes no arguments");
|
||||
return nullptr;
|
||||
}
|
||||
return BPyGPUVertFormat_CreatePyObject(nullptr);
|
||||
}
|
||||
|
||||
static uint attr_size(GPUVertCompType type, int len)
|
||||
{
|
||||
if (type == GPU_COMP_I10) {
|
||||
return 4; /* Always packed as 10_10_10_2. */
|
||||
}
|
||||
BLI_assert(type <= GPU_COMP_F32); /* Other types have irregular sizes (not bytes). */
|
||||
const uint sizes[] = {1, 1, 2, 2, 4, 4, 4};
|
||||
return len * sizes[type];
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertformat_attr_add_doc,
|
||||
".. method:: attr_add(id, comp_type, len, fetch_mode)\n"
|
||||
"\n"
|
||||
" Add a new attribute to the format.\n"
|
||||
"\n"
|
||||
" :param id: Name of the attribute. Often ``position``, ``normal``, ...\n"
|
||||
" :type id: str\n"
|
||||
" :param comp_type: The data type that will be used to store the value in memory.\n"
|
||||
" :type comp_type: Literal['I8', 'U8', 'I16', 'U16', 'I32', 'U32', 'F32', 'I10']\n"
|
||||
" :param len: How many individual values the attribute consists of\n"
|
||||
" (e.g. 2 for uv coordinates).\n"
|
||||
" :type len: int\n"
|
||||
" :param fetch_mode: How values from memory will be converted when used in the shader.\n"
|
||||
" This is mainly useful for memory optimizations when you want to store values with\n"
|
||||
" reduced precision. E.g. you can store a float in only 1 byte but it will be\n"
|
||||
" converted to a normal 4 byte float when used.\n"
|
||||
" :type fetch_mode: Literal['FLOAT', 'INT', 'INT_TO_FLOAT_UNIT']\n");
|
||||
static PyObject *pygpu_vertformat_attr_add(BPyGPUVertFormat *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
const char *id;
|
||||
uint len;
|
||||
PyC_StringEnum comp_type = {pygpu_vertcomptype_items, GPU_COMP_I8};
|
||||
PyC_StringEnum fetch_mode = {pygpu_vertfetchmode_items, GPU_FETCH_FLOAT};
|
||||
|
||||
if (self->fmt.attr_len == GPU_VERT_ATTR_MAX_LEN) {
|
||||
PyErr_SetString(PyExc_ValueError, "Maximum attr reached " STRINGIFY(GPU_VERT_ATTR_MAX_LEN));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char *_keywords[] = {"id", "comp_type", "len", "fetch_mode", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"$" /* Keyword only arguments. */
|
||||
"s" /* `id` */
|
||||
"O&" /* `comp_type` */
|
||||
"I" /* `len` */
|
||||
"O&" /* `fetch_mode` */
|
||||
":attr_add",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kwds,
|
||||
&_parser,
|
||||
&id,
|
||||
PyC_ParseStringEnum,
|
||||
&comp_type,
|
||||
&len,
|
||||
PyC_ParseStringEnum,
|
||||
&fetch_mode))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPUVertCompType comp_type_enum = GPUVertCompType(comp_type.value_found);
|
||||
GPUVertFetchMode fetch_mode_enum = GPUVertFetchMode(fetch_mode.value_found);
|
||||
|
||||
if (len > 4) {
|
||||
PyErr_WarnEx(
|
||||
PyExc_DeprecationWarning,
|
||||
"Using GPUVertFormat.attr_add(...) with component count greater than 4 is deprecated. "
|
||||
"Use several attributes for each matrix columns instead.",
|
||||
1);
|
||||
}
|
||||
|
||||
if (attr_size(comp_type_enum, len) % 4 != 0) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Using GPUVertFormat.attr_add(...) with a format that is not 4 bytes aligned is "
|
||||
"deprecated. Add padding components and/or higher precision integers.",
|
||||
1);
|
||||
}
|
||||
|
||||
uint attr_id = GPU_vertformat_attr_add_legacy(
|
||||
&self->fmt, id, comp_type_enum, len, fetch_mode_enum);
|
||||
|
||||
return PyLong_FromLong(attr_id);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_vertformat__tp_methods[] = {
|
||||
{"attr_add",
|
||||
reinterpret_cast<PyCFunction>(pygpu_vertformat_attr_add),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_vertformat_attr_add_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static void pygpu_vertformat__tp_dealloc(BPyGPUVertFormat *self)
|
||||
{
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertformat__tp_doc,
|
||||
".. class:: GPUVertFormat()\n"
|
||||
"\n"
|
||||
" This object contains information about the structure of a vertex buffer.\n");
|
||||
PyTypeObject BPyGPUVertFormat_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUVertFormat",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUVertFormat),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_vertformat__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_vertformat__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_vertformat__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_vertformat__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUVertFormat_CreatePyObject(GPUVertFormat *fmt)
|
||||
{
|
||||
BPyGPUVertFormat *self;
|
||||
|
||||
self = PyObject_New(BPyGPUVertFormat, &BPyGPUVertFormat_Type);
|
||||
if (fmt) {
|
||||
self->fmt = *fmt;
|
||||
}
|
||||
else {
|
||||
memset(&self->fmt, 0, sizeof(self->fmt));
|
||||
}
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_vertex_format.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern PyTypeObject BPyGPUVertFormat_Type;
|
||||
|
||||
#define BPyGPUVertFormat_Check(v) (Py_TYPE(v) == &BPyGPUVertFormat_Type)
|
||||
|
||||
struct BPyGPUVertFormat {
|
||||
PyObject_HEAD
|
||||
GPUVertFormat fmt;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUVertFormat_CreatePyObject(GPUVertFormat *fmt);
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user