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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGermano Cavalcante <mano-wii>2022-04-13 00:28:27 +0300
committerGermano Cavalcante <germano.costa@ig.com.br>2022-04-13 00:50:56 +0300
commit9bc678969aaef5e2343d9362648e9a633d1b6e5e (patch)
tree6967bc7469d26d91925bddc618d4ad69432d12fc
parent359b6baf325a701328732598ecc04b68a9a335d9 (diff)
pyGPU: Port 'StageInterfaceInfo' and 'ShaderCreateInfo' types
In order to allow GLSL Cross Compilation across platforms, expose in Python the `GPUShaderCreateInfo` strategy as detailed in https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation The new features can be listed as follows: ``` >>> gpu.types.GPUShaderCreateInfo. define( fragment_out( fragment_source( push_constant( sampler( typedef_source( uniform_buf( vertex_in( vertex_out( vertex_source( >>> gpu.types.GPUStageInterfaceInfo. flat( name no_perspective( smooth( >>> gpu.shader.create_from_info( ``` Reviewed By: fclem, campbellbarton Differential Revision: https://developer.blender.org/D14497
-rw-r--r--source/blender/gpu/GPU_shader.h1
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc39
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.cc28
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.hh2
-rw-r--r--source/blender/python/generic/py_capi_utils.h8
-rw-r--r--source/blender/python/gpu/CMakeLists.txt1
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c36
-rw-r--r--source/blender/python/gpu/gpu_py_shader.h47
-rw-r--r--source/blender/python/gpu/gpu_py_shader_create_info.cc1095
-rw-r--r--source/blender/python/gpu/gpu_py_types.c8
10 files changed, 1243 insertions, 22 deletions
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index c0c25022836..eed7685bf01 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -56,6 +56,7 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info);
GPUShader *GPU_shader_create_from_info_name(const char *info_name);
const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name);
+bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128]);
struct GPU_ShaderCreateFromArray_Params {
const char **vert, **geom, **frag, **defs;
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index b434cfbbb0e..227525bd5d3 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -249,6 +249,19 @@ const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name)
return gpu_shader_create_info_get(info_name);
}
+bool GPU_shader_create_info_check_error(const GPUShaderCreateInfo *_info, char r_error[128])
+{
+ using namespace blender::gpu::shader;
+ const ShaderCreateInfo &info = *reinterpret_cast<const ShaderCreateInfo *>(_info);
+ std::string error = info.check_error();
+ if (error.length() == 0) {
+ return true;
+ }
+
+ BLI_strncpy(r_error, error.c_str(), 128);
+ return false;
+}
+
GPUShader *GPU_shader_create_from_info_name(const char *info_name)
{
using namespace blender::gpu::shader;
@@ -270,28 +283,10 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
GPU_debug_group_begin(GPU_DEBUG_SHADER_COMPILATION_GROUP);
- /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
- if (info.compute_source_.is_empty()) {
- if (info.vertex_source_.is_empty()) {
- printf("Missing vertex shader in %s.\n", info.name_.c_str());
- }
- if (info.fragment_source_.is_empty()) {
- printf("Missing fragment shader in %s.\n", info.name_.c_str());
- }
- BLI_assert(!info.vertex_source_.is_empty() && !info.fragment_source_.is_empty());
- }
- else {
- if (!info.vertex_source_.is_empty()) {
- printf("Compute shader has vertex_source_ shader attached in %s.\n", info.name_.c_str());
- }
- if (!info.geometry_source_.is_empty()) {
- printf("Compute shader has geometry_source_ shader attached in %s.\n", info.name_.c_str());
- }
- if (!info.fragment_source_.is_empty()) {
- printf("Compute shader has fragment_source_ shader attached in %s.\n", info.name_.c_str());
- }
- BLI_assert(info.vertex_source_.is_empty() && info.geometry_source_.is_empty() &&
- info.fragment_source_.is_empty());
+ std::string error = info.check_error();
+ if (error.length()) {
+ printf(error.c_str());
+ BLI_assert(true);
}
Shader *shader = GPUBackend::get()->shader_alloc(info.name_.c_str());
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc
index 0dd82d4ea44..6e82201b424 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.cc
+++ b/source/blender/gpu/intern/gpu_shader_create_info.cc
@@ -136,6 +136,34 @@ void ShaderCreateInfo::finalize()
}
}
+std::string ShaderCreateInfo::check_error() const
+{
+ std::string error;
+
+ /* At least a vertex shader and a fragment shader are required, or only a compute shader. */
+ if (this->compute_source_.is_empty()) {
+ if (this->vertex_source_.is_empty()) {
+ error += "Missing vertex shader in " + this->name_ + ".\n";
+ }
+ if (this->fragment_source_.is_empty()) {
+ error += "Missing fragment shader in " + this->name_ + ".\n";
+ }
+ }
+ else {
+ if (!this->vertex_source_.is_empty()) {
+ error += "Compute shader has vertex_source_ shader attached in" + this->name_ + ".\n";
+ }
+ if (!this->geometry_source_.is_empty()) {
+ error += "Compute shader has geometry_source_ shader attached in" + this->name_ + ".\n";
+ }
+ if (!this->fragment_source_.is_empty()) {
+ error += "Compute shader has fragment_source_ shader attached in" + this->name_ + ".\n";
+ }
+ }
+
+ return error;
+}
+
void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info)
{
if (!auto_resource_location_) {
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh
index 51008993353..dfd73219d1b 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.hh
+++ b/source/blender/gpu/intern/gpu_shader_create_info.hh
@@ -787,6 +787,8 @@ struct ShaderCreateInfo {
/* WARNING: Recursive. */
void finalize();
+ std::string check_error() const;
+
/** Error detection that some backend compilers do not complain about. */
void validate(const ShaderCreateInfo &other_info);
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index 0f94b264159..ecb6db2b82c 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -8,6 +8,10 @@
#ifndef __PY_CAPI_UTILS_H__
#define __PY_CAPI_UTILS_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "BLI_sys_types.h"
#include "BLI_utildefines_variadic.h"
@@ -273,3 +277,7 @@ bool PyC_StructFmt_type_is_byte(char format);
bool PyC_StructFmt_type_is_bool(char format);
#endif /* __PY_CAPI_UTILS_H__ */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt
index 644bbd18173..e726cb7883d 100644
--- a/source/blender/python/gpu/CMakeLists.txt
+++ b/source/blender/python/gpu/CMakeLists.txt
@@ -29,6 +29,7 @@ set(SRC
gpu_py_offscreen.c
gpu_py_platform.c
gpu_py_select.c
+ gpu_py_shader_create_info.cc
gpu_py_shader.c
gpu_py_state.c
gpu_py_texture.c
diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c
index c74b3e173d1..cdcf22df7a1 100644
--- a/source/blender/python/gpu/gpu_py_shader.c
+++ b/source/blender/python/gpu/gpu_py_shader.c
@@ -831,6 +831,38 @@ static PyObject *pygpu_shader_code_from_builtin(BPyGPUShader *UNUSED(self), PyOb
return r_dict;
}
+PyDoc_STRVAR(pygpu_shader_create_from_info_doc,
+ ".. function:: create_from_info(shader_info)\n"
+ "\n"
+ " Create shader from a GPUShaderCreateInfo.\n"
+ "\n"
+ " :param shader_info: GPUShaderCreateInfo\n"
+ " :type shader_info: :class:`bpy.types.GPUShaderCreateInfo`\n"
+ " :return: Shader object corresponding to the given name.\n"
+ " :rtype: :class:`bpy.types.GPUShader`\n");
+static PyObject *pygpu_shader_create_from_info(BPyGPUShader *UNUSED(self),
+ BPyGPUShaderCreateInfo *o)
+{
+ if (!BPyGPUShaderCreateInfo_Check(o)) {
+ PyErr_Format(PyExc_TypeError, "Expected a GPUShaderCreateInfo, got %s", Py_TYPE(o)->tp_name);
+ return NULL;
+ }
+
+ char error[128];
+ if (!GPU_shader_create_info_check_error(o->info, error)) {
+ PyErr_SetString(PyExc_Exception, error);
+ return NULL;
+ }
+
+ GPUShader *shader = GPU_shader_create_from_info(o->info);
+ if (!shader) {
+ PyErr_SetString(PyExc_Exception, "Shader Compile Error, see console for more details");
+ return NULL;
+ }
+
+ return BPyGPUShader_CreatePyObject(shader, false);
+}
+
static struct PyMethodDef pygpu_shader_module__tp_methods[] = {
{"unbind", (PyCFunction)pygpu_shader_unbind, METH_NOARGS, pygpu_shader_unbind_doc},
{"from_builtin",
@@ -841,6 +873,10 @@ static struct PyMethodDef pygpu_shader_module__tp_methods[] = {
(PyCFunction)pygpu_shader_code_from_builtin,
METH_O,
pygpu_shader_code_from_builtin_doc},
+ {"create_from_info",
+ (PyCFunction)pygpu_shader_create_from_info,
+ METH_O,
+ pygpu_shader_create_from_info_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_shader.h b/source/blender/python/gpu/gpu_py_shader.h
index eb4810efeac..b5944c4b3a0 100644
--- a/source/blender/python/gpu/gpu_py_shader.h
+++ b/source/blender/python/gpu/gpu_py_shader.h
@@ -6,6 +6,12 @@
#pragma once
+/* 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.c */
+
extern PyTypeObject BPyGPUShader_Type;
#define BPyGPUShader_Check(v) (Py_TYPE(v) == &BPyGPUShader_Type)
@@ -18,3 +24,44 @@ typedef struct BPyGPUShader {
PyObject *BPyGPUShader_CreatePyObject(struct GPUShader *shader, bool is_builtin);
PyObject *bpygpu_shader_init(void);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* gpu_py_shader_create_info.cc */
+
+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)
+
+typedef struct BPyGPUStageInterfaceInfo {
+ PyObject_VAR_HEAD
+ struct GPUStageInterfaceInfo *interface;
+#ifdef USE_GPU_PY_REFERENCES
+ /* Just to keep a user to prevent freeing buf's we're using. */
+ PyObject *references;
+#endif
+} BPyGPUStageInterfaceInfo;
+
+typedef struct BPyGPUShaderCreateInfo {
+ PyObject_VAR_HEAD
+ struct GPUShaderCreateInfo *info;
+#ifdef USE_GPU_PY_REFERENCES
+ /* Just to keep a user to prevent freeing buf's we're using. */
+ PyObject *vertex_source;
+ PyObject *fragment_source;
+ PyObject *typedef_source;
+ PyObject *references;
+#endif
+ size_t constants_total_size;
+} BPyGPUShaderCreateInfo;
+
+PyObject *BPyGPUStageInterfaceInfo_CreatePyObject(struct GPUStageInterfaceInfo *interface);
+PyObject *BPyGPUShaderCreateInfo_CreatePyObject(struct GPUShaderCreateInfo *info);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/python/gpu/gpu_py_shader_create_info.cc b/source/blender/python/gpu/gpu_py_shader_create_info.cc
new file mode 100644
index 00000000000..caf71ad5980
--- /dev/null
+++ b/source/blender/python/gpu/gpu_py_shader_create_info.cc
@@ -0,0 +1,1095 @@
+/* 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 "BLI_utildefines.h"
+
+#include "GPU_shader.h"
+#include "intern/gpu_shader_create_info.hh"
+
+#include "../generic/py_capi_utils.h"
+#include "../generic/python_utildefines.h"
+
+#include "gpu_py_shader.h" /* own include */
+
+//#define USE_PYGPU_SHADER_INFO_IMAGE_METHOD
+
+using blender::gpu::shader::DualBlend;
+using blender::gpu::shader::Frequency;
+using blender::gpu::shader::ImageType;
+using blender::gpu::shader::ShaderCreateInfo;
+using blender::gpu::shader::StageInterfaceInfo;
+using blender::gpu::shader::Type;
+
+#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD
+using blender::gpu::shader::Qualifier;
+
+# define PYDOC_QUALIFIERS \
+ "- ``NO_RESTRICT``\n" \
+ "- ``READ``\n" \
+ "- ``WRITE``\n"
+static const struct PyC_FlagSet pygpu_qualifiers[] = {
+ {(int)Qualifier::NO_RESTRICT, "NO_RESTRICT"},
+ {(int)Qualifier::READ, "READ"},
+ {(int)Qualifier::WRITE, "WRITE"},
+ {0, nullptr},
+};
+#endif
+
+#define PYDOC_TYPE_LIST \
+ "- ``FLOAT``\n" \
+ "- ``VEC2``\n" \
+ "- ``VEC3``\n" \
+ "- ``VEC4``\n" \
+ "- ``MAT3``\n" \
+ "- ``MAT4``\n" \
+ "- ``UINT``\n" \
+ "- ``UVEC2``\n" \
+ "- ``UVEC3``\n" \
+ "- ``UVEC4``\n" \
+ "- ``INT``\n" \
+ "- ``IVEC2``\n" \
+ "- ``IVEC3``\n" \
+ "- ``IVEC4``\n" \
+ "- ``BOOL``\n"
+static const struct PyC_StringEnumItems pygpu_attrtype_items[] = {
+ {(int)Type::FLOAT, "FLOAT"},
+ {(int)Type::VEC2, "VEC2"},
+ {(int)Type::VEC3, "VEC3"},
+ {(int)Type::VEC4, "VEC4"},
+ {(int)Type::MAT3, "MAT3"},
+ {(int)Type::MAT4, "MAT4"},
+ {(int)Type::UINT, "UINT"},
+ {(int)Type::UVEC2, "UVEC2"},
+ {(int)Type::UVEC3, "UVEC3"},
+ {(int)Type::UVEC4, "UVEC4"},
+ {(int)Type::INT, "INT"},
+ {(int)Type::IVEC2, "IVEC2"},
+ {(int)Type::IVEC3, "IVEC3"},
+ {(int)Type::IVEC4, "IVEC4"},
+ {(int)Type::BOOL, "BOOL"},
+ {0, nullptr},
+};
+
+#define PYDOC_IMAGE_TYPES \
+ "- ``FLOAT_BUFFER``\n" \
+ "- ``FLOAT_1D``\n" \
+ "- ``FLOAT_1D_ARRAY``\n" \
+ "- ``FLOAT_2D``\n" \
+ "- ``FLOAT_2D_ARRAY``\n" \
+ "- ``FLOAT_3D``\n" \
+ "- ``FLOAT_CUBE``\n" \
+ "- ``FLOAT_CUBE_ARRAY``\n" \
+ "- ``INT_BUFFER``\n" \
+ "- ``INT_1D``\n" \
+ "- ``INT_1D_ARRAY``\n" \
+ "- ``INT_2D``\n" \
+ "- ``INT_2D_ARRAY``\n" \
+ "- ``INT_3D``\n" \
+ "- ``INT_CUBE``\n" \
+ "- ``INT_CUBE_ARRAY``\n" \
+ "- ``UINT_BUFFER``\n" \
+ "- ``UINT_1D``\n" \
+ "- ``UINT_1D_ARRAY``\n" \
+ "- ``UINT_2D``\n" \
+ "- ``UINT_2D_ARRAY``\n" \
+ "- ``UINT_3D``\n" \
+ "- ``UINT_CUBE``\n" \
+ "- ``UINT_CUBE_ARRAY``\n" \
+ "- ``SHADOW_2D``\n" \
+ "- ``SHADOW_2D_ARRAY``\n" \
+ "- ``SHADOW_CUBE``\n" \
+ "- ``SHADOW_CUBE_ARRAY``\n" \
+ "- ``DEPTH_2D``\n" \
+ "- ``DEPTH_2D_ARRAY``\n" \
+ "- ``DEPTH_CUBE``\n" \
+ "- ``DEPTH_CUBE_ARRAY``\n"
+static const struct PyC_StringEnumItems pygpu_imagetype_items[] = {
+ {(int)ImageType::FLOAT_BUFFER, "FLOAT_BUFFER"},
+ {(int)ImageType::FLOAT_1D, "FLOAT_1D"},
+ {(int)ImageType::FLOAT_1D_ARRAY, "FLOAT_1D_ARRAY"},
+ {(int)ImageType::FLOAT_2D, "FLOAT_2D"},
+ {(int)ImageType::FLOAT_2D_ARRAY, "FLOAT"},
+ {(int)ImageType::FLOAT_3D, "FLOAT_2D_ARRAY"},
+ {(int)ImageType::FLOAT_CUBE, "FLOAT_CUBE"},
+ {(int)ImageType::FLOAT_CUBE_ARRAY, "FLOAT_CUBE_ARRAY"},
+ {(int)ImageType::INT_BUFFER, "INT_BUFFER"},
+ {(int)ImageType::INT_1D, "INT_1D"},
+ {(int)ImageType::INT_1D_ARRAY, "INT_1D_ARRAY"},
+ {(int)ImageType::INT_2D, "INT_2D"},
+ {(int)ImageType::INT_2D_ARRAY, "INT_2D_ARRAY"},
+ {(int)ImageType::INT_3D, "INT_3D"},
+ {(int)ImageType::INT_CUBE, "INT_CUBE"},
+ {(int)ImageType::INT_CUBE_ARRAY, "INT_CUBE_ARRAY"},
+ {(int)ImageType::UINT_BUFFER, "UINT_BUFFER"},
+ {(int)ImageType::UINT_1D, "UINT_1D"},
+ {(int)ImageType::UINT_1D_ARRAY, "UINT_1D_ARRAY"},
+ {(int)ImageType::UINT_2D, "UINT_2D"},
+ {(int)ImageType::UINT_2D_ARRAY, "UINT_2D_ARRAY"},
+ {(int)ImageType::UINT_3D, "UINT_3D"},
+ {(int)ImageType::UINT_CUBE, "UINT_CUBE"},
+ {(int)ImageType::UINT_CUBE_ARRAY, "UINT_CUBE_ARRAY"},
+ {(int)ImageType::SHADOW_2D, "SHADOW_2D"},
+ {(int)ImageType::SHADOW_2D_ARRAY, "SHADOW_2D_ARRAY"},
+ {(int)ImageType::SHADOW_CUBE, "SHADOW_CUBE"},
+ {(int)ImageType::SHADOW_CUBE_ARRAY, "SHADOW_CUBE_ARRAY"},
+ {(int)ImageType::DEPTH_2D, "DEPTH_2D"},
+ {(int)ImageType::DEPTH_2D_ARRAY, "DEPTH_2D_ARRAY"},
+ {(int)ImageType::DEPTH_CUBE, "DEPTH_CUBE"},
+ {(int)ImageType::DEPTH_CUBE_ARRAY, "DEPTH_CUBE_ARRAY"},
+ {0, nullptr},
+};
+
+static const struct PyC_StringEnumItems pygpu_dualblend_items[] = {
+ {(int)DualBlend::NONE, "NONE"},
+ {(int)DualBlend::SRC_0, "SRC_0"},
+ {(int)DualBlend::SRC_1, "SRC_1"},
+ {0, nullptr},
+};
+
+/* -------------------------------------------------------------------- */
+/** \name GPUStageInterfaceInfo Methods
+ * \{ */
+
+static bool pygpu_interface_info_get_args(BPyGPUStageInterfaceInfo *self,
+ PyObject *args,
+ const char *format,
+ Type *r_type,
+ const char **r_name)
+{
+ struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items};
+ PyObject *py_name;
+
+ if (!PyArg_ParseTuple(args, format, PyC_ParseStringEnum, &pygpu_type, &py_name)) {
+ return false;
+ }
+
+ const char *name = PyUnicode_AsUTF8(py_name);
+ if (name == nullptr) {
+ return false;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, (PyObject *)py_name);
+#endif
+
+ *r_type = (Type)pygpu_type.value_found;
+ *r_name = name;
+ return true;
+}
+
+PyDoc_STRVAR(pygpu_interface_info_smooth_doc,
+ ".. method:: smooth(type, name)\n"
+ "\n"
+ " Add an attribute with qualifier of type `smooth` to the interface block.\n"
+ "\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ " :type type: str\n"
+ " :param name: name of the attribute.\n"
+ " :type name: str\n");
+static PyObject *pygpu_interface_info_smooth(BPyGPUStageInterfaceInfo *self, PyObject *args)
+{
+ Type type;
+ const char *name;
+ if (!pygpu_interface_info_get_args(self, args, "O&O:smooth", &type, &name)) {
+ return nullptr;
+ }
+
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface);
+ interface->smooth(type, name);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_interface_info_flat_doc,
+ ".. method:: flat(type, name)\n"
+ "\n"
+ " Add an attribute with qualifier of type `flat` to the interface block.\n"
+ "\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ " :type type: str\n"
+ " :param name: name of the attribute.\n"
+ " :type name: str\n");
+static PyObject *pygpu_interface_info_flat(BPyGPUStageInterfaceInfo *self, PyObject *args)
+{
+ Type type;
+ const char *name;
+ if (!pygpu_interface_info_get_args(self, args, "O&O:flat", &type, &name)) {
+ return nullptr;
+ }
+
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface);
+ interface->flat(type, name);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(
+ pygpu_interface_info_no_perspective_doc,
+ ".. method:: no_perspective(type, name)\n"
+ "\n"
+ " Add an attribute with qualifier of type `no_perspective` to the interface block.\n"
+ "\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ " :type type: str\n"
+ " :param name: name of the attribute.\n"
+ " :type name: str\n");
+static PyObject *pygpu_interface_info_no_perspective(BPyGPUStageInterfaceInfo *self,
+ PyObject *args)
+{
+ Type type;
+ const char *name;
+ if (!pygpu_interface_info_get_args(self, args, "O&O:no_perspective", &type, &name)) {
+ return nullptr;
+ }
+
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface);
+ interface->no_perspective(type, name);
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef pygpu_interface_info__tp_methods[] = {
+ {"smooth",
+ (PyCFunction)pygpu_interface_info_smooth,
+ METH_VARARGS,
+ pygpu_interface_info_smooth_doc},
+ {"flat", (PyCFunction)pygpu_interface_info_flat, METH_VARARGS, pygpu_interface_info_flat_doc},
+ {"no_perspective",
+ (PyCFunction)pygpu_interface_info_no_perspective,
+ METH_VARARGS,
+ pygpu_interface_info_no_perspective_doc},
+ {nullptr, nullptr, 0, nullptr},
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUStageInterfaceInfo Getters and Setters
+ * \{ */
+
+PyDoc_STRVAR(pygpu_interface_info_name_doc,
+ "Name of the interface block.\n"
+ "\n"
+ ":type: str");
+static PyObject *pygpu_interface_info_name_get(BPyGPUStageInterfaceInfo *self,
+ void *UNUSED(closure))
+{
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(self->interface);
+ return PyUnicode_FromString(interface->name.c_str());
+}
+
+static PyGetSetDef pygpu_interface_info__tp_getseters[] = {
+ {"name",
+ (getter)pygpu_interface_info_name_get,
+ (setter) nullptr,
+ pygpu_interface_info_name_doc,
+ nullptr},
+ {nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUStageInterfaceInfo Type
+ * \{ */
+
+static PyObject *pygpu_interface_info__tp_new(PyTypeObject *UNUSED(type),
+ PyObject *args,
+ PyObject *kwds)
+{
+ if (kwds) {
+ PyErr_SetString(PyExc_TypeError, "no keywords are expected");
+ return nullptr;
+ }
+
+ const char *name;
+ if (!PyArg_ParseTuple(args, "s:GPUStageInterfaceInfo.__new__*", &name)) {
+ return nullptr;
+ }
+
+ StageInterfaceInfo *interface = new StageInterfaceInfo(name, "");
+ GPUStageInterfaceInfo *interface_info = reinterpret_cast<GPUStageInterfaceInfo *>(interface);
+
+ auto self = BPyGPUStageInterfaceInfo_CreatePyObject(interface_info);
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject *py_name = PyTuple_GET_ITEM(args, 0);
+ PyList_Append(((BPyGPUStageInterfaceInfo *)self)->references, py_name);
+#endif
+
+ return self;
+}
+
+#ifdef USE_GPU_PY_REFERENCES
+
+static int pygpu_interface_info__tp_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self);
+ Py_VISIT(py_interface->references);
+ return 0;
+}
+
+static int pygpu_interface_info__tp_clear(PyObject *self)
+{
+ BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self);
+ Py_CLEAR(py_interface->references);
+ return 0;
+}
+
+#endif
+
+static void pygpu_interface_info__tp_dealloc(PyObject *self)
+{
+ BPyGPUStageInterfaceInfo *py_interface = reinterpret_cast<BPyGPUStageInterfaceInfo *>(self);
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(py_interface->interface);
+ delete interface;
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject_GC_UnTrack(self);
+ if (py_interface->references) {
+ pygpu_interface_info__tp_clear(self);
+ Py_CLEAR(py_interface->references);
+ }
+#endif
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+PyDoc_STRVAR(pygpu_interface_info__tp_doc,
+ ".. class:: GPUStageInterfaceInfo(name)\n"
+ "\n"
+ " List of varyings between shader stages.\n\n"
+ "\n"
+ " :param name: Name of the interface block.\n"
+ " :type value: str\n");
+constexpr PyTypeObject pygpu_interface_info_type()
+{
+ PyTypeObject pytype = {PyVarObject_HEAD_INIT(nullptr, 0)};
+ pytype.tp_name = "GPUStageInterfaceInfo";
+ pytype.tp_basicsize = sizeof(BPyGPUStageInterfaceInfo);
+ pytype.tp_dealloc = pygpu_interface_info__tp_dealloc;
+ pytype.tp_doc = pygpu_interface_info__tp_doc;
+#ifdef USE_GPU_PY_REFERENCES
+ pytype.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC;
+ pytype.tp_traverse = pygpu_interface_info__tp_traverse;
+ pytype.tp_clear = pygpu_interface_info__tp_clear;
+#else
+ pytype.tp_flags = Py_TPFLAGS_DEFAULT,
+#endif
+ pytype.tp_methods = pygpu_interface_info__tp_methods;
+ pytype.tp_getset = pygpu_interface_info__tp_getseters;
+ pytype.tp_new = pygpu_interface_info__tp_new;
+ return pytype;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUShaderCreateInfo Methods
+ * \{ */
+
+PyDoc_STRVAR(pygpu_shader_info_vertex_in_doc,
+ ".. method:: vertex_in(slot, type, name)\n"
+ "\n"
+ " Add a vertex shader input attribute.\n"
+ "\n"
+ " :param slot: The attribute index.\n"
+ " :type slot: int\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ " :type type: str\n"
+ " :param name: name of the attribute.\n"
+ " :type name: str\n");
+static PyObject *pygpu_shader_info_vertex_in(BPyGPUShaderCreateInfo *self, PyObject *args)
+{
+ int slot;
+ struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items};
+ const char *param;
+
+ if (!PyArg_ParseTuple(args, "iO&s:vertex_in", &slot, PyC_ParseStringEnum, &pygpu_type, &param)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject *py_name = PyTuple_GET_ITEM(args, 2);
+ PyList_Append(self->references, py_name);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->vertex_in(slot, (Type)pygpu_type.value_found, param);
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_shader_info_vertex_out_doc,
+ ".. method:: vertex_out(interface)\n"
+ "\n"
+ " Add a vertex shader output interface block.\n"
+ "\n"
+ " :param interface: Object describing the block.\n"
+ " :type interface: :class:`gpu.types.GPUStageInterfaceInfo`\n");
+static PyObject *pygpu_shader_info_vertex_out(BPyGPUShaderCreateInfo *self,
+ BPyGPUStageInterfaceInfo *o)
+{
+ if (!BPyGPUStageInterfaceInfo_Check(o)) {
+ PyErr_Format(PyExc_TypeError, "Expected a GPUStageInterfaceInfo, got %s", Py_TYPE(o)->tp_name);
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, (PyObject *)o);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ StageInterfaceInfo *interface = reinterpret_cast<StageInterfaceInfo *>(o->interface);
+ info->vertex_out(*interface);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_shader_info_fragment_out_doc,
+ ".. method:: fragment_out(slot, type, name, blend='NONE')\n"
+ "\n"
+ " Specify a fragment output corresponding to a framebuffer target slot.\n"
+ "\n"
+ " :param slot: The attribute index.\n"
+ " :type slot: int\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ " :type type: str\n"
+ " :param name: Name of the attribute.\n"
+ " :type name: str\n"
+ " :param blend: Dual Source Blending Index. It can be 'NONE', 'SRC_0' or 'SRC_1'.\n"
+ " :type blend: str\n");
+static PyObject *pygpu_shader_info_fragment_out(BPyGPUShaderCreateInfo *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ int slot;
+ struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items};
+ const char *name;
+ struct PyC_StringEnum blend_type = {pygpu_dualblend_items, (int)DualBlend::NONE};
+
+ static const char *_keywords[] = {"slot", "type", "name", "blend", nullptr};
+ static _PyArg_Parser _parser = {"iO&s|$O&:fragment_out", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ &slot,
+ PyC_ParseStringEnum,
+ &pygpu_type,
+ &name,
+ PyC_ParseStringEnum,
+ &blend_type)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject *py_name = PyTuple_GET_ITEM(args, 2);
+ PyList_Append(self->references, py_name);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->fragment_out(slot, (Type)pygpu_type.value_found, name, (DualBlend)blend_type.value_found);
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(
+ pygpu_shader_info_uniform_buf_doc,
+ ".. method:: uniform_buf(slot, type_name, name)\n"
+ "\n"
+ " Specify a uniform variable whose type can be one of those declared in `typedef_source`.\n"
+ "\n"
+ " :param slot: The uniform variable index.\n"
+ " :type slot: int\n"
+ " :param type_name: Name of the data type. It can be a struct type defined in the source "
+ "passed through the :meth:`gpu.types.GPUShaderCreateInfo.typedef_source`.\n"
+ " :type type_name: str\n"
+ " :param name: The uniform variable name.\n"
+ " :type name: str\n");
+static PyObject *pygpu_shader_info_uniform_buf(BPyGPUShaderCreateInfo *self, PyObject *args)
+{
+ int slot;
+ const char *type_name;
+ const char *name;
+
+ if (!PyArg_ParseTuple(args, "iss:uniform_buf", &slot, &type_name, &name)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 1)); /* type_name */
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 2)); /* name */
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->uniform_buf(slot, type_name, name);
+
+ Py_RETURN_NONE;
+}
+
+#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD
+PyDoc_STRVAR(
+ pygpu_shader_info_image_doc,
+ ".. method:: image(slot, format, type, name, qualifiers={'NO_RESTRICT'})\n"
+ "\n"
+ " Specify an image resource used for arbitrary load and store operations.\n"
+ "\n"
+ " :param slot: The image resource index.\n"
+ " :type slot: int\n"
+ " :param format: The GPUTexture format that is passed to the shader. Possible values are:\n"
+ "" PYDOC_TEX_FORMAT_ITEMS
+ " :type format: str\n"
+ " :param type: The data type describing how the image is to be read in the shader. "
+ "Possible values are:\n"
+ "" PYDOC_IMAGE_TYPES
+ " :type type: str\n"
+ " :param name: The image resource name.\n"
+ " :type name: str\n"
+ " :param qualifiers: Set containing values that describe how the image resource is to be "
+ "read or written. Possible values are:\n"
+ "" PYDOC_QUALIFIERS
+ ""
+ " :type qualifiers: set\n");
+static PyObject *pygpu_shader_info_image(BPyGPUShaderCreateInfo *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ int slot;
+ struct PyC_StringEnum pygpu_texformat = {pygpu_textureformat_items};
+ struct PyC_StringEnum pygpu_imagetype = {pygpu_imagetype_items};
+ const char *name;
+ PyObject *py_qualifiers = nullptr;
+ Qualifier qualifier = Qualifier::NO_RESTRICT;
+
+ static const char *_keywords[] = {"slot", "format", "type", "name", "qualifiers", nullptr};
+ static _PyArg_Parser _parser = {"iO&O&s|$O:image", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kwds,
+ &_parser,
+ &slot,
+ PyC_ParseStringEnum,
+ &pygpu_texformat,
+ PyC_ParseStringEnum,
+ &pygpu_imagetype,
+ &name,
+ &py_qualifiers)) {
+ return nullptr;
+ }
+
+ if (py_qualifiers &&
+ PyC_FlagSet_ToBitfield(
+ pygpu_qualifiers, py_qualifiers, (int *)&qualifier, "shader_info.image") == -1) {
+ return nullptr;
+ }
+
+# ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 3)); /* name */
+# endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->image(slot,
+ (eGPUTextureFormat)pygpu_texformat.value_found,
+ qualifier,
+ (ImageType)pygpu_imagetype.value_found,
+ name);
+
+ Py_RETURN_NONE;
+}
+#endif
+
+PyDoc_STRVAR(
+ pygpu_shader_info_sampler_doc,
+ ".. method:: sampler(slot, type, name)\n"
+ "\n"
+ " Specify an image texture sampler.\n"
+ "\n"
+ " :param slot: The image texture sampler index.\n"
+ " :type slot: int\n"
+ " :param type: The data type describing the format of each sampler unit. Possible values "
+ "are:\n"
+ "" PYDOC_IMAGE_TYPES
+ " :type type: str\n"
+ " :param name: The image texture sampler name.\n"
+ " :type name: str\n");
+static PyObject *pygpu_shader_info_sampler(BPyGPUShaderCreateInfo *self, PyObject *args)
+{
+ int slot;
+ struct PyC_StringEnum pygpu_samplertype = {pygpu_imagetype_items};
+ const char *name;
+
+ if (!PyArg_ParseTuple(
+ args, "iO&s:sampler", &slot, PyC_ParseStringEnum, &pygpu_samplertype, &name)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 2)); /* name */
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->sampler(slot, (ImageType)pygpu_samplertype.value_found, name);
+
+ Py_RETURN_NONE;
+}
+
+static int constant_type_size(Type type)
+{
+ switch (type) {
+ case Type::BOOL:
+ case Type::FLOAT:
+ case Type::INT:
+ case Type::UINT:
+ return 4;
+ break;
+ case Type::VEC2:
+ case Type::UVEC2:
+ case Type::IVEC2:
+ return 8;
+ break;
+ case Type::VEC3:
+ case Type::UVEC3:
+ case Type::IVEC3:
+ return 12;
+ break;
+ case Type::VEC4:
+ case Type::UVEC4:
+ case Type::IVEC4:
+ return 16;
+ break;
+ case Type::MAT3:
+ return 72 + 3 * 4;
+ case Type::MAT4:
+ return 128;
+ break;
+ }
+ BLI_assert(false);
+ return -1;
+}
+
+static int constants_calc_size(ShaderCreateInfo *info)
+{
+ int size_prev = 0;
+ int size_last = 0;
+ for (const ShaderCreateInfo::PushConst &uniform : info->push_constants_) {
+ int pad = 0;
+ int size = constant_type_size(uniform.type);
+ if (size_last && size_last != size) {
+ /* Calc pad. */
+ int pack = (size == 8) ? 8 : 16;
+ if (size_last < size) {
+ pad = pack - (size_last % pack);
+ }
+ else {
+ pad = size_prev % pack;
+ }
+ }
+ else if (size == 12) {
+ /* It is still unclear how Vulkan handles padding for `vec3` constants. For now let's follow
+ * the rules of the `std140` layout. */
+ pad = 4;
+ }
+ size_prev += pad + size * std::max(1, uniform.array_size);
+ size_last = size;
+ }
+ return size_prev + (size_prev % 16);
+}
+
+PyDoc_STRVAR(pygpu_shader_info_push_constant_doc,
+ ".. method:: push_constant(type, name, size=0)\n"
+ "\n"
+ " Specify a global access constant.\n"
+ "\n"
+ " :param type: One of these types:\n"
+ "\n" PYDOC_TYPE_LIST
+ " :type type: str\n"
+ " :param name: Name of the constant.\n"
+ " :type name: str\n"
+ " :param size: If not zero, indicates that the constant is an array with the "
+ "specified size.\n"
+ " :type size: uint\n");
+static PyObject *pygpu_shader_info_push_constant(BPyGPUShaderCreateInfo *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ struct PyC_StringEnum pygpu_type = {pygpu_attrtype_items};
+ const char *name = nullptr;
+ int array_size = 0;
+
+ static const char *_keywords[] = {"type", "name", "size", nullptr};
+ static _PyArg_Parser _parser = {"O&s|I:push_constant", _keywords, 0};
+ if (!_PyArg_ParseTupleAndKeywordsFast(
+ args, kwds, &_parser, PyC_ParseStringEnum, &pygpu_type, &name, &array_size)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject *py_name = PyTuple_GET_ITEM(args, 1);
+ PyList_Append(self->references, py_name);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->push_constant((Type)pygpu_type.value_found, name, array_size);
+
+#define VULKAN_LIMIT 128
+ int size = constants_calc_size(info);
+ if (size > VULKAN_LIMIT) {
+ printf("Push constants have a minimum supported size of "
+ STRINGIFY(VULKAN_LIMIT)
+ " bytes, however the constants added so far already reach %d bytes. Consider using UBO.\n", size);
+ }
+#undef VULKAN_LIMIT
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(
+ pygpu_shader_info_vertex_source_doc,
+ ".. method:: vertex_source(source)\n"
+ "\n"
+ " Vertex shader source code written in GLSL.\n"
+ "\n"
+ " Example:\n"
+ "\n"
+ ".. code-block:: python\n"
+ "\n"
+ " \"void main {gl_Position = vec4(pos, 1.0);}\"\n"
+ "\n"
+ ".. seealso:: `GLSL Cross Compilation "
+ "<https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation>`__"
+ "\n"
+ " :param source: The vertex shader source code.\n"
+ " :type source: str\n");
+static PyObject *pygpu_shader_info_vertex_source(BPyGPUShaderCreateInfo *self, PyObject *o)
+{
+ const char *vertex_source = PyUnicode_AsUTF8(o);
+ if (vertex_source == nullptr) {
+ PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ if (self->vertex_source) {
+ Py_DECREF(self->vertex_source);
+ }
+
+ self->vertex_source = o;
+ Py_INCREF(o);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->vertex_source("common_colormanagement_lib.glsl");
+ info->vertex_source_generated = vertex_source;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(
+ pygpu_shader_info_fragment_source_doc,
+ ".. method:: fragment_source(source)\n"
+ "\n"
+ " Fragment shader source code written in GLSL.\n"
+ "\n"
+ " Example:\n"
+ "\n"
+ ".. code-block:: python\n"
+ "\n"
+ " \"void main {fragColor = vec4(0.0, 0.0, 0.0, 1.0);}\"\n"
+ "\n"
+ ".. seealso:: `GLSL Cross Compilation "
+ "<https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation>`__"
+ "\n"
+ " :param source: The fragment shader source code.\n"
+ " :type source: str\n");
+static PyObject *pygpu_shader_info_fragment_source(BPyGPUShaderCreateInfo *self, PyObject *o)
+{
+ const char *fragment_source = PyUnicode_AsUTF8(o);
+ if (fragment_source == nullptr) {
+ PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ if (self->fragment_source) {
+ Py_DECREF(self->fragment_source);
+ }
+
+ self->fragment_source = o;
+ Py_INCREF(o);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ info->fragment_source("common_colormanagement_lib.glsl");
+ info->fragment_source_generated = fragment_source;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_shader_info_typedef_source_doc,
+ ".. method:: typedef_source(source)\n"
+ "\n"
+ " Source code included before resource declaration. "
+ "Useful for defining structs used by Uniform Buffers.\n"
+ "\n"
+ " Example:\n"
+ "\n"
+ ".. code-block:: python\n"
+ "\n"
+ " \"struct MyType {int foo; float bar;};\"\n"
+ "\n"
+ " :param source: The source code defining types.\n"
+ " :type source: str\n");
+static PyObject *pygpu_shader_info_typedef_source(BPyGPUShaderCreateInfo *self, PyObject *o)
+{
+ const char *typedef_source = PyUnicode_AsUTF8(o);
+ if (typedef_source == nullptr) {
+ PyErr_Format(PyExc_ValueError, "expected a string, got %s", Py_TYPE(o)->tp_name);
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ if (self->typedef_source) {
+ Py_DECREF(self->typedef_source);
+ }
+
+ self->typedef_source = o;
+ Py_INCREF(o);
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+#if 0
+ if (info->typedef_sources_.is_empty()) {
+ info->typedef_source("GPU_shader_shared_utils.h");
+ }
+#endif
+ info->typedef_source_generated = typedef_source;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(pygpu_shader_info_define_doc,
+ ".. method:: define(name, value)\n"
+ "\n"
+ " Add a preprocessing define directive. In GLSL it would be something like:\n"
+ "\n"
+ ".. code-block:: glsl\n"
+ "\n"
+ " #define name value\n"
+ "\n"
+ " :param name: Token name.\n"
+ " :type name: str\n"
+ " :param value: Text that replaces token occurrences.\n"
+ " :type value: str\n");
+static PyObject *pygpu_shader_info_define(BPyGPUShaderCreateInfo *self, PyObject *args)
+{
+ const char *name;
+ const char *value = nullptr;
+
+ if (!PyArg_ParseTuple(args, "s|s:define", &name, &value)) {
+ return nullptr;
+ }
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 0)); /* name */
+ if (value) {
+ PyList_Append(self->references, PyTuple_GET_ITEM(args, 1)); /* value */
+ }
+#endif
+
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(self->info);
+ if (value) {
+ info->define(name, value);
+ }
+ else {
+ info->define(name);
+ }
+
+ Py_RETURN_NONE;
+}
+
+static struct PyMethodDef pygpu_shader_info__tp_methods[] = {
+ {"vertex_in",
+ (PyCFunction)pygpu_shader_info_vertex_in,
+ METH_VARARGS,
+ pygpu_shader_info_vertex_in_doc},
+ {"vertex_out",
+ (PyCFunction)pygpu_shader_info_vertex_out,
+ METH_O,
+ pygpu_shader_info_vertex_out_doc},
+ {"fragment_out",
+ (PyCFunction)(void *)pygpu_shader_info_fragment_out,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_shader_info_fragment_out_doc},
+ {"uniform_buf",
+ (PyCFunction)(void *)pygpu_shader_info_uniform_buf,
+ METH_VARARGS,
+ pygpu_shader_info_uniform_buf_doc},
+#ifdef USE_PYGPU_SHADER_INFO_IMAGE_METHOD
+ {"image",
+ (PyCFunction)(void *)pygpu_shader_info_image,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_shader_info_image_doc},
+#endif
+ {"sampler",
+ (PyCFunction)pygpu_shader_info_sampler,
+ METH_VARARGS,
+ pygpu_shader_info_sampler_doc},
+ {"push_constant",
+ (PyCFunction)(void *)pygpu_shader_info_push_constant,
+ METH_VARARGS | METH_KEYWORDS,
+ pygpu_shader_info_push_constant_doc},
+ {"vertex_source",
+ (PyCFunction)pygpu_shader_info_vertex_source,
+ METH_O,
+ pygpu_shader_info_vertex_source_doc},
+ {"fragment_source",
+ (PyCFunction)pygpu_shader_info_fragment_source,
+ METH_O,
+ pygpu_shader_info_fragment_source_doc},
+ {"typedef_source",
+ (PyCFunction)pygpu_shader_info_typedef_source,
+ METH_O,
+ pygpu_shader_info_typedef_source_doc},
+ {"define", (PyCFunction)pygpu_shader_info_define, METH_VARARGS, pygpu_shader_info_define_doc},
+ {nullptr, nullptr, 0, nullptr},
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name GPUShaderCreateInfo Init
+ * \{ */
+
+static PyObject *pygpu_shader_info__tp_new(PyTypeObject *UNUSED(type),
+ PyObject *args,
+ PyObject *kwds)
+{
+ if (PyTuple_Size(args) || kwds) {
+ PyErr_SetString(PyExc_TypeError, "no args or keywords are expected");
+ return nullptr;
+ }
+
+ ShaderCreateInfo *info = new ShaderCreateInfo("pyGPU_Shader");
+ GPUShaderCreateInfo *shader_info = reinterpret_cast<GPUShaderCreateInfo *>(info);
+
+ return BPyGPUShaderCreateInfo_CreatePyObject(shader_info);
+}
+
+#ifdef USE_GPU_PY_REFERENCES
+
+static int pygpu_shader_info__tp_traverse(PyObject *self, visitproc visit, void *arg)
+{
+ BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self);
+ Py_VISIT(py_info->vertex_source);
+ Py_VISIT(py_info->fragment_source);
+ Py_VISIT(py_info->references);
+ return 0;
+}
+
+static int pygpu_shader_info__tp_clear(PyObject *self)
+{
+ BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self);
+ Py_CLEAR(py_info->vertex_source);
+ Py_CLEAR(py_info->fragment_source);
+ Py_CLEAR(py_info->references);
+ return 0;
+}
+
+#endif
+
+static void pygpu_shader_info__tp_dealloc(PyObject *self)
+{
+ BPyGPUShaderCreateInfo *py_info = reinterpret_cast<BPyGPUShaderCreateInfo *>(self);
+ ShaderCreateInfo *info = reinterpret_cast<ShaderCreateInfo *>(py_info->info);
+ delete info;
+
+#ifdef USE_GPU_PY_REFERENCES
+ PyObject_GC_UnTrack(self);
+ if (py_info->references || py_info->vertex_source || py_info->fragment_source) {
+ pygpu_shader_info__tp_clear(self);
+ Py_XDECREF(py_info->vertex_source);
+ Py_XDECREF(py_info->fragment_source);
+ Py_XDECREF(py_info->references);
+ }
+#endif
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+PyDoc_STRVAR(pygpu_shader_info__tp_doc,
+ ".. class:: GPUShaderCreateInfo()\n"
+ "\n"
+ " Stores and describes types and variables that are used in shader sources.\n");
+constexpr PyTypeObject pygpu_shader_info_type()
+{
+ PyTypeObject pytype = {PyVarObject_HEAD_INIT(nullptr, 0)};
+ pytype.tp_name = "GPUShaderCreateInfo";
+ pytype.tp_basicsize = sizeof(BPyGPUShaderCreateInfo);
+ pytype.tp_dealloc = pygpu_shader_info__tp_dealloc;
+ pytype.tp_doc = pygpu_shader_info__tp_doc;
+#ifdef USE_GPU_PY_REFERENCES
+ pytype.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC;
+ pytype.tp_traverse = pygpu_shader_info__tp_traverse;
+ pytype.tp_clear = pygpu_shader_info__tp_clear;
+#else
+ pytype.tp_flags = Py_TPFLAGS_DEFAULT,
+#endif
+ pytype.tp_methods = pygpu_shader_info__tp_methods;
+ pytype.tp_new = pygpu_shader_info__tp_new;
+ return pytype;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Public API
+ * \{ */
+
+PyTypeObject BPyGPUStageInterfaceInfo_Type = pygpu_interface_info_type();
+PyTypeObject BPyGPUShaderCreateInfo_Type = pygpu_shader_info_type();
+
+PyObject *BPyGPUStageInterfaceInfo_CreatePyObject(GPUStageInterfaceInfo *interface)
+{
+ BPyGPUStageInterfaceInfo *self;
+
+#ifdef USE_GPU_PY_REFERENCES
+ self = (BPyGPUStageInterfaceInfo *)_PyObject_GC_New(&BPyGPUStageInterfaceInfo_Type);
+ self->references = PyList_New(0);
+#else
+ self = PyObject_New(BPyGPUStageInterfaceInfo, &BPyGPUStageInterfaceInfo_Type);
+#endif
+
+ self->interface = interface;
+
+ return (PyObject *)self;
+}
+
+PyObject *BPyGPUShaderCreateInfo_CreatePyObject(GPUShaderCreateInfo *info)
+{
+ BPyGPUShaderCreateInfo *self;
+
+#ifdef USE_GPU_PY_REFERENCES
+ self = (BPyGPUShaderCreateInfo *)_PyObject_GC_New(&BPyGPUShaderCreateInfo_Type);
+ self->vertex_source = nullptr;
+ self->fragment_source = nullptr;
+ self->typedef_source = nullptr;
+ self->references = PyList_New(0);
+#else
+ self = PyObject_New(BPyGPUShaderCreateInfo, &BPyGPUShaderCreateInfo_Type);
+#endif
+
+ self->info = info;
+ self->constants_total_size = 0;
+
+ return (PyObject *)self;
+}
+
+/** \} */
diff --git a/source/blender/python/gpu/gpu_py_types.c b/source/blender/python/gpu/gpu_py_types.c
index 6dff70ad530..65201df4a9e 100644
--- a/source/blender/python/gpu/gpu_py_types.c
+++ b/source/blender/python/gpu/gpu_py_types.c
@@ -59,6 +59,12 @@ PyObject *bpygpu_types_init(void)
if (PyType_Ready(&BPyGPUUniformBuf_Type) < 0) {
return NULL;
}
+ if (PyType_Ready(&BPyGPUShaderCreateInfo_Type) < 0) {
+ return NULL;
+ }
+ if (PyType_Ready(&BPyGPUStageInterfaceInfo_Type) < 0) {
+ return NULL;
+ }
PyModule_AddType(submodule, &BPyGPU_BufferType);
PyModule_AddType(submodule, &BPyGPUVertFormat_Type);
@@ -70,6 +76,8 @@ PyObject *bpygpu_types_init(void)
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);
return submodule;
}