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
This commit is contained in:
Germano Cavalcante 2022-04-12 18:28:27 -03:00 committed by Germano Cavalcante
parent 359b6baf32
commit 9bc678969a
Notes: blender-bot 2023-02-14 05:12:59 +01:00
Referenced by commit 927cb5bfac, Cleanup: separate format-units for Python argument parsing
10 changed files with 1243 additions and 22 deletions

View File

@ -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;

View File

@ -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());

View File

@ -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_) {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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},
};

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;
}