This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/python/gpu/gpu_py_shader_create_info.cc
Jeroen Bakker fd9b197226 GPU: Fix using FLOAT_2D_ARRAY and FLOAT_3D textures via Python.
Translation from python enum values were incorrect and textures created
in python using those types would result in faulty textures. In
renderdoc those textures would not bind.
2022-12-06 20:16:39 +01:00

1142 lines
36 KiB
C++

/* 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 "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"
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_2D_ARRAY"},
{int(ImageType::FLOAT_3D), "FLOAT_3D"},
{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"
" :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
" :arg 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"
" :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
" :arg 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"
" :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
" :arg 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 * /*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 * /*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"
" :arg 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"
" :arg slot: The attribute index.\n"
" :type slot: int\n"
" :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
" :arg 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"
" :arg 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"
" :arg slot: The attribute index.\n"
" :type slot: int\n"
" :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
" :arg name: Name of the attribute.\n"
" :type name: str\n"
" :arg 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 = {
"i" /* `slot` */
"O&" /* `type` */
"s" /* `name` */
"|$" /* Optional keyword only arguments. */
"O&" /* `blend` */
":fragment_out",
_keywords,
nullptr,
};
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"
" :arg slot: The uniform variable index.\n"
" :type slot: int\n"
" :arg 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"
" :arg 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"
" :arg slot: The image resource index.\n"
" :type slot: int\n"
" :arg format: The GPUTexture format that is passed to the shader. Possible values are:\n"
"" PYDOC_TEX_FORMAT_ITEMS
" :type format: str\n"
" :arg type: The data type describing how the image is to be read in the shader. "
"Possible values are:\n"
"\n" PYDOC_IMAGE_TYPES
"\n"
" :type type: str\n"
" :arg name: The image resource name.\n"
" :type name: str\n"
" :arg 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 = {
"i" /* `slot` */
"O&" /* `format` */
"O&" /* `type` */
"s" /* `name` */
"|$" /* Optional keyword only arguments. */
"O" /* `qualifiers` */
":image",
_keywords,
nullptr,
};
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"
" :arg slot: The image texture sampler index.\n"
" :type slot: int\n"
" :arg type: The data type describing the format of each sampler unit. Possible values "
"are:\n"
"\n" PYDOC_IMAGE_TYPES
"\n"
" :type type: str\n"
" :arg 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:
case Type::UCHAR4:
case Type::CHAR4:
case blender::gpu::shader::Type::VEC3_101010I2:
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 36 + 3 * 4;
case Type::MAT4:
return 64;
break;
case blender::gpu::shader::Type::UCHAR:
case blender::gpu::shader::Type::CHAR:
return 1;
break;
case blender::gpu::shader::Type::UCHAR2:
case blender::gpu::shader::Type::CHAR2:
return 2;
break;
case blender::gpu::shader::Type::UCHAR3:
case blender::gpu::shader::Type::CHAR3:
return 3;
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"
" :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
" :arg name: Name of the constant.\n"
" :type name: str\n"
" :arg 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&" /* `type` */
"s" /* `name` */
"|" /* Optional arguments. */
"I" /* `size` */
":push_constant",
_keywords,
nullptr,
};
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"
" :arg source: The vertex shader source code.\n"
" :type source: str\n"
"\n"
" .. seealso:: `GLSL Cross Compilation "
"<https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation>`__\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"
" :arg source: The fragment shader source code.\n"
" :type source: str\n"
"\n"
" .. seealso:: `GLSL Cross Compilation "
"<https://wiki.blender.org/wiki/EEVEE_%26_Viewport/GPU_Module/GLSL_Cross_Compilation>`__\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"
" :arg 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"
" :arg name: Token name.\n"
" :type name: str\n"
" :arg 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 Initialization
* \{ */
static PyObject *pygpu_shader_info__tp_new(PyTypeObject * /*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;
}
/** \} */