GPUShader: GL backend isolation
This commit is contained in:
parent
df28d2c27e
commit
216d78687d
Notes:
blender-bot
2023-02-14 06:17:14 +01:00
Referenced by commit 8948f73784
, Fix T81334: Python view-port drawing depth-test regression
Referenced by issue #83203, bgl.glGetUniformLocation() wrongly always returns -1
Referenced by issue #81334, viewport drawing with gpu module
Referenced by issue #79975, Blender 2.91 crashes with segmentation fault when enabling Sun Position Addon
Referenced by issue #79978, Crashes after start
Referenced by issue #79951, Blender 2.91 Alpha crash related to GPU_shader_create_from_python
|
@ -31,8 +31,7 @@
|
|||
|
||||
#include "GPU_extensions.h"
|
||||
#include "GPU_platform.h"
|
||||
#include "intern/gpu_primitive_private.h"
|
||||
#include "intern/gpu_shader_private.h"
|
||||
#include "GPU_shader.h"
|
||||
|
||||
#ifdef USE_GPU_SELECT
|
||||
# include "GPU_select.h"
|
||||
|
@ -821,8 +820,8 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup,
|
|||
break;
|
||||
case DRW_UNIFORM_TFEEDBACK_TARGET:
|
||||
BLI_assert(uni->pvalue && (*use_tfeedback == false));
|
||||
*use_tfeedback = GPU_shader_transform_feedback_enable(
|
||||
shgroup->shader, ((GPUVertBuf *)uni->pvalue)->vbo_id);
|
||||
*use_tfeedback = GPU_shader_transform_feedback_enable(shgroup->shader,
|
||||
((GPUVertBuf *)uni->pvalue));
|
||||
break;
|
||||
/* Legacy/Fallback support. */
|
||||
case DRW_UNIFORM_BASE_INSTANCE:
|
||||
|
|
|
@ -92,6 +92,7 @@ set(SRC
|
|||
opengl/gl_batch.cc
|
||||
opengl/gl_context.cc
|
||||
opengl/gl_drawlist.cc
|
||||
opengl/gl_shader.cc
|
||||
opengl/gl_vertex_array.cc
|
||||
|
||||
GPU_attr_binding.h
|
||||
|
@ -137,13 +138,14 @@ set(SRC
|
|||
intern/gpu_primitive_private.h
|
||||
intern/gpu_private.h
|
||||
intern/gpu_select_private.h
|
||||
intern/gpu_shader_private.h
|
||||
intern/gpu_shader_private.hh
|
||||
intern/gpu_vertex_format_private.h
|
||||
|
||||
opengl/gl_backend.hh
|
||||
opengl/gl_batch.hh
|
||||
opengl/gl_context.hh
|
||||
opengl/gl_drawlist.hh
|
||||
opengl/gl_shader.hh
|
||||
opengl/gl_vertex_array.hh
|
||||
)
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GPUShaderInterface;
|
||||
struct GPUShader;
|
||||
|
||||
void GPU_matrix_reset(void); /* to Identity transform & empty stack */
|
||||
|
||||
|
@ -147,7 +147,7 @@ const float (*GPU_matrix_normal_get(float m[3][3]))[3];
|
|||
const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3];
|
||||
|
||||
/* set uniform values for currently bound shader */
|
||||
void GPU_matrix_bind(const struct GPUShaderInterface *);
|
||||
void GPU_matrix_bind(struct GPUShader *shader);
|
||||
bool GPU_matrix_dirty_get(void); /* since last bind */
|
||||
|
||||
/* own working polygon offset */
|
||||
|
|
|
@ -27,14 +27,19 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct GPUShader GPUShader;
|
||||
struct GPUShaderInterface;
|
||||
struct GPUTexture;
|
||||
struct GPUUniformBuffer;
|
||||
struct GPUVertBuf;
|
||||
|
||||
/* GPU Shader
|
||||
* - only for fragment shaders now
|
||||
* - must call texture bind before setting a texture as uniform! */
|
||||
/* TODO(fclem) These members should be private and the
|
||||
* whole struct should just be an opaque pointer. */
|
||||
typedef struct GPUShader {
|
||||
/** Uniform & attribute locations for shader. */
|
||||
struct GPUShaderInterface *interface;
|
||||
/** For debugging purpose. */
|
||||
char name[64];
|
||||
} GPUShader;
|
||||
|
||||
typedef enum eGPUShaderTFBType {
|
||||
GPU_SHADER_TFB_NONE = 0, /* Transform feedback unsupported. */
|
||||
|
@ -63,10 +68,7 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode,
|
|||
const char **tf_names,
|
||||
const int tf_count,
|
||||
const char *shader_name);
|
||||
GPUShader *GPU_shader_load_from_binary(const char *binary,
|
||||
const int binary_format,
|
||||
const int binary_len,
|
||||
const char *shname);
|
||||
|
||||
struct GPU_ShaderCreateFromArray_Params {
|
||||
const char **vert, **geom, **frag, **defs;
|
||||
};
|
||||
|
@ -81,12 +83,12 @@ void GPU_shader_bind(GPUShader *shader);
|
|||
void GPU_shader_unbind(void);
|
||||
|
||||
/* Returns true if transform feedback was successfully enabled. */
|
||||
bool GPU_shader_transform_feedback_enable(GPUShader *shader, unsigned int vbo_id);
|
||||
bool GPU_shader_transform_feedback_enable(GPUShader *shader, struct GPUVertBuf *vertbuf);
|
||||
void GPU_shader_transform_feedback_disable(GPUShader *shader);
|
||||
|
||||
int GPU_shader_get_program(GPUShader *shader);
|
||||
|
||||
void GPU_shader_set_srgb_uniform(const struct GPUShaderInterface *interface);
|
||||
void GPU_shader_set_srgb_uniform(GPUShader *shader);
|
||||
|
||||
int GPU_shader_get_uniform(GPUShader *shader, const char *name);
|
||||
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "gpu_batch_private.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_drawlist_private.hh"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
|
@ -43,7 +44,7 @@ class GPUBackend {
|
|||
virtual Batch *batch_alloc(void) = 0;
|
||||
virtual DrawList *drawlist_alloc(int list_length) = 0;
|
||||
// virtual FrameBuffer *framebuffer_alloc(void) = 0;
|
||||
// virtual Shader *shader_alloc(void) = 0;
|
||||
virtual Shader *shader_alloc(const char *name) = 0;
|
||||
// virtual Texture *texture_alloc(void) = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#include "gpu_batch_private.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_primitive_private.h"
|
||||
#include "gpu_shader_private.h"
|
||||
#include "gpu_shader_private.hh"
|
||||
#include "gpu_vertex_format_private.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include "GPU_batch.h"
|
||||
#include "GPU_batch_presets.h" /* own include */
|
||||
#include "GPU_batch_utils.h"
|
||||
#include "gpu_shader_private.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Local Structures
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
#include "GPU_batch.h"
|
||||
#include "GPU_batch_utils.h" /* own include */
|
||||
#include "gpu_shader_private.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Polygon Creation (2D)
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include "gpu_attr_binding_private.h"
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_primitive_private.h"
|
||||
#include "gpu_shader_private.h"
|
||||
#include "gpu_shader_private.hh"
|
||||
#include "gpu_vertex_format_private.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -145,10 +145,7 @@ GPUVertFormat *immVertexFormat(void)
|
|||
|
||||
void immBindShader(GPUShader *shader)
|
||||
{
|
||||
#if TRUST_NO_ONE
|
||||
assert(imm.bound_program == NULL);
|
||||
assert(glIsProgram(shader->program));
|
||||
#endif
|
||||
BLI_assert(imm.bound_program == NULL);
|
||||
|
||||
imm.bound_program = shader;
|
||||
imm.shader_interface = shader->interface;
|
||||
|
@ -159,8 +156,8 @@ void immBindShader(GPUShader *shader)
|
|||
|
||||
GPU_shader_bind(shader);
|
||||
get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface);
|
||||
GPU_matrix_bind(imm.shader_interface);
|
||||
GPU_shader_set_srgb_uniform(imm.shader_interface);
|
||||
GPU_matrix_bind(shader);
|
||||
GPU_shader_set_srgb_uniform(shader);
|
||||
}
|
||||
|
||||
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
|
||||
|
@ -375,7 +372,7 @@ static void immDrawSetup(void)
|
|||
}
|
||||
|
||||
if (GPU_matrix_dirty_get()) {
|
||||
GPU_matrix_bind(imm.shader_interface);
|
||||
GPU_matrix_bind(imm.bound_program);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -643,13 +643,13 @@ const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3]
|
|||
return m;
|
||||
}
|
||||
|
||||
void GPU_matrix_bind(const GPUShaderInterface *shaderface)
|
||||
void GPU_matrix_bind(GPUShader *shader)
|
||||
{
|
||||
/* set uniform values to matrix stack values
|
||||
* call this before a draw call if desired matrices are dirty
|
||||
* call glUseProgram before this, as glUniform expects program to be bound
|
||||
*/
|
||||
|
||||
const GPUShaderInterface *shaderface = shader->interface;
|
||||
int32_t MV = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW);
|
||||
int32_t P = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION);
|
||||
int32_t MVP = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MVP);
|
||||
|
@ -658,32 +658,30 @@ void GPU_matrix_bind(const GPUShaderInterface *shaderface)
|
|||
int32_t MV_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW_INV);
|
||||
int32_t P_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION_INV);
|
||||
|
||||
/* XXX(fclem) this works but this assumes shader is unused inside GPU_shader_uniform_vector. */
|
||||
GPUShader *sh = NULL;
|
||||
if (MV != -1) {
|
||||
GPU_shader_uniform_vector(sh, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL));
|
||||
GPU_shader_uniform_vector(shader, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL));
|
||||
}
|
||||
if (P != -1) {
|
||||
GPU_shader_uniform_vector(sh, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL));
|
||||
GPU_shader_uniform_vector(shader, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL));
|
||||
}
|
||||
if (MVP != -1) {
|
||||
GPU_shader_uniform_vector(
|
||||
sh, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL));
|
||||
shader, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL));
|
||||
}
|
||||
if (N != -1) {
|
||||
GPU_shader_uniform_vector(sh, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL));
|
||||
GPU_shader_uniform_vector(shader, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL));
|
||||
}
|
||||
if (MV_inv != -1) {
|
||||
Mat4 m;
|
||||
GPU_matrix_model_view_get(m);
|
||||
invert_m4(m);
|
||||
GPU_shader_uniform_vector(sh, MV_inv, 16, 1, (const float *)m);
|
||||
GPU_shader_uniform_vector(shader, MV_inv, 16, 1, (const float *)m);
|
||||
}
|
||||
if (P_inv != -1) {
|
||||
Mat4 m;
|
||||
GPU_matrix_projection_get(m);
|
||||
invert_m4(m);
|
||||
GPU_shader_uniform_vector(sh, P_inv, 16, 1, (const float *)m);
|
||||
GPU_shader_uniform_vector(shader, P_inv, 16, 1, (const float *)m);
|
||||
}
|
||||
|
||||
gpu_matrix_state_active_set_dirty(false);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BKE_appdir.h"
|
||||
#include "BKE_global.h"
|
||||
|
@ -42,182 +43,44 @@
|
|||
#include "GPU_texture.h"
|
||||
#include "GPU_uniformbuffer.h"
|
||||
|
||||
#include "gpu_backend.hh"
|
||||
#include "gpu_context_private.hh"
|
||||
#include "gpu_shader_private.h"
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[];
|
||||
|
||||
/* Adjust these constants as needed. */
|
||||
#define MAX_DEFINE_LENGTH 256
|
||||
#define MAX_EXT_DEFINE_LENGTH 512
|
||||
|
||||
#ifndef NDEBUG
|
||||
static uint g_shaderid = 0;
|
||||
#endif
|
||||
using namespace blender;
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Convenience functions
|
||||
/** \name Debug functions
|
||||
* \{ */
|
||||
|
||||
static void shader_print_errors(const char *task, const char *log, const char **code, int totcode)
|
||||
void Shader::print_errors(Span<const char *> sources, const char *log)
|
||||
{
|
||||
int line = 1;
|
||||
// int line = 1;
|
||||
|
||||
fprintf(stderr, "GPUShader: %s error:\n", task);
|
||||
// for (int i = 0; i < totcode; i++) {
|
||||
// const char *c, *pos, *end = code[i] + strlen(code[i]);
|
||||
|
||||
for (int i = 0; i < totcode; i++) {
|
||||
const char *c, *pos, *end = code[i] + strlen(code[i]);
|
||||
// if (G.debug & G_DEBUG) {
|
||||
// fprintf(stderr, "===== shader string %d ====\n", i + 1);
|
||||
|
||||
if (G.debug & G_DEBUG) {
|
||||
fprintf(stderr, "===== shader string %d ====\n", i + 1);
|
||||
|
||||
c = code[i];
|
||||
while ((c < end) && (pos = strchr(c, '\n'))) {
|
||||
fprintf(stderr, "%2d ", line);
|
||||
fwrite(c, (pos + 1) - c, 1, stderr);
|
||||
c = pos + 1;
|
||||
line++;
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s", c);
|
||||
}
|
||||
}
|
||||
// c = code[i];
|
||||
// while ((c < end) && (pos = strchr(c, '\n'))) {
|
||||
// fprintf(stderr, "%2d ", line);
|
||||
// fwrite(c, (pos + 1) - c, 1, stderr);
|
||||
// c = pos + 1;
|
||||
// line++;
|
||||
// }
|
||||
|
||||
// fprintf(stderr, "%s", c);
|
||||
// }
|
||||
// }
|
||||
/* TODO display the error lines context. */
|
||||
fprintf(stderr, "%s\n", log);
|
||||
}
|
||||
|
||||
static const char *gpu_shader_version(void)
|
||||
{
|
||||
return "#version 330\n";
|
||||
}
|
||||
|
||||
static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH])
|
||||
{
|
||||
/* enable extensions for features that are not part of our base GLSL version
|
||||
* don't use an extension for something already available!
|
||||
*/
|
||||
|
||||
if (GLEW_ARB_texture_gather) {
|
||||
/* There is a bug on older Nvidia GPU where GL_ARB_texture_gather
|
||||
* is reported to be supported but yield a compile error (see T55802). */
|
||||
if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) {
|
||||
strcat(defines, "#extension GL_ARB_texture_gather: enable\n");
|
||||
|
||||
/* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the
|
||||
* shader so double check the preprocessor define (see T56544). */
|
||||
if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) {
|
||||
strcat(defines, "#ifdef GL_ARB_texture_gather\n");
|
||||
strcat(defines, "# define GPU_ARB_texture_gather\n");
|
||||
strcat(defines, "#endif\n");
|
||||
}
|
||||
else {
|
||||
strcat(defines, "#define GPU_ARB_texture_gather\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GLEW_ARB_texture_query_lod) {
|
||||
/* a #version 400 feature, but we use #version 330 maximum so use extension */
|
||||
strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n");
|
||||
}
|
||||
if (GLEW_ARB_shader_draw_parameters) {
|
||||
strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n");
|
||||
strcat(defines, "#define GPU_ARB_shader_draw_parameters\n");
|
||||
}
|
||||
if (GPU_arb_texture_cube_map_array_is_supported()) {
|
||||
strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n");
|
||||
strcat(defines, "#define GPU_ARB_texture_cube_map_array\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH])
|
||||
{
|
||||
/* some useful defines to detect GPU type */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
strcat(defines, "#define GPU_ATI\n");
|
||||
if (GPU_crappy_amd_driver()) {
|
||||
strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n");
|
||||
}
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
strcat(defines, "#define GPU_NVIDIA\n");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
strcat(defines, "#define GPU_INTEL\n");
|
||||
}
|
||||
|
||||
/* some useful defines to detect OS type */
|
||||
if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
strcat(defines, "#define OS_WIN\n");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) {
|
||||
strcat(defines, "#define OS_MAC\n");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
|
||||
strcat(defines, "#define OS_UNIX\n");
|
||||
}
|
||||
|
||||
float derivatives_factors[2];
|
||||
GPU_get_dfdy_factors(derivatives_factors);
|
||||
if (derivatives_factors[0] == 1.0f) {
|
||||
strcat(defines, "#define DFDX_SIGN 1.0\n");
|
||||
}
|
||||
else {
|
||||
strcat(defines, "#define DFDX_SIGN -1.0\n");
|
||||
}
|
||||
|
||||
if (derivatives_factors[1] == 1.0f) {
|
||||
strcat(defines, "#define DFDY_SIGN 1.0\n");
|
||||
}
|
||||
else {
|
||||
strcat(defines, "#define DFDY_SIGN -1.0\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define DEBUG_SHADER_NONE ""
|
||||
#define DEBUG_SHADER_VERTEX "vert"
|
||||
#define DEBUG_SHADER_FRAGMENT "frag"
|
||||
#define DEBUG_SHADER_GEOMETRY "geom"
|
||||
|
||||
/**
|
||||
* Dump GLSL shaders to disk
|
||||
*
|
||||
* This is used for profiling shader performance externally and debug if shader code is correct.
|
||||
* If called with no code, it simply bumps the shader index, so different shaders for the same
|
||||
* program share the same index.
|
||||
*/
|
||||
static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension)
|
||||
{
|
||||
if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We use the same shader index for shaders in the same program.
|
||||
* So we call this function once before calling for the individual shaders. */
|
||||
static int shader_index = 0;
|
||||
if (code == NULL) {
|
||||
shader_index++;
|
||||
BLI_assert(STREQ(DEBUG_SHADER_NONE, extension));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine the full path of the new shader. */
|
||||
char shader_path[FILE_MAX];
|
||||
|
||||
char file_name[512] = {'\0'};
|
||||
sprintf(file_name, "%04d.%s", shader_index, extension);
|
||||
|
||||
BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name);
|
||||
|
||||
/* Write shader to disk. */
|
||||
FILE *f = fopen(shader_path, "w");
|
||||
if (f == NULL) {
|
||||
printf("Error writing to file: %s\n", shader_path);
|
||||
}
|
||||
for (int j = 0; j < num_shaders; j++) {
|
||||
fprintf(f, "%s", code[j]);
|
||||
}
|
||||
fclose(f);
|
||||
printf("Shader file written to disk: %s\n", shader_path);
|
||||
UNUSED_VARS(sources);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -226,20 +89,149 @@ static void gpu_dump_shaders(const char **code, const int num_shaders, const cha
|
|||
/** \name Creation / Destruction
|
||||
* \{ */
|
||||
|
||||
GPUShader *GPU_shader_create(const char *vertexcode,
|
||||
Shader::Shader(const char *sh_name)
|
||||
{
|
||||
BLI_strncpy(this->name, sh_name, sizeof(this->name));
|
||||
}
|
||||
|
||||
Shader::~Shader()
|
||||
{
|
||||
if (this->interface) {
|
||||
GPU_shaderinterface_discard(this->interface);
|
||||
}
|
||||
}
|
||||
|
||||
static void standard_defines(Vector<const char *> &sources)
|
||||
{
|
||||
BLI_assert(sources.size() == 0);
|
||||
/* Version needs to be first. Exact values will be added by implementation. */
|
||||
sources.append("version");
|
||||
/* some useful defines to detect GPU type */
|
||||
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
sources.append("#define GPU_ATI\n");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
sources.append("#define GPU_NVIDIA\n");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
sources.append("#define GPU_INTEL\n");
|
||||
}
|
||||
/* some useful defines to detect OS type */
|
||||
if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
sources.append("#define OS_WIN\n");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) {
|
||||
sources.append("#define OS_MAC\n");
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) {
|
||||
sources.append("#define OS_UNIX\n");
|
||||
}
|
||||
|
||||
if (GPU_crappy_amd_driver()) {
|
||||
sources.append("#define GPU_DEPRECATED_AMD_DRIVER\n");
|
||||
}
|
||||
}
|
||||
|
||||
GPUShader *GPU_shader_create_ex(const char *vertcode,
|
||||
const char *fragcode,
|
||||
const char *geomcode,
|
||||
const char *libcode,
|
||||
const char *defines,
|
||||
const eGPUShaderTFBType tf_type,
|
||||
const char **tf_names,
|
||||
const int tf_count,
|
||||
const char *shname)
|
||||
{
|
||||
/* At least a vertex shader and a fragment shader are required. */
|
||||
BLI_assert((fragcode != NULL) && (vertcode != NULL));
|
||||
|
||||
Shader *shader = GPUBackend::get()->shader_alloc(shname);
|
||||
|
||||
if (vertcode) {
|
||||
Vector<const char *> sources;
|
||||
standard_defines(sources);
|
||||
sources.append("#define GPU_VERTEX_SHADER\n");
|
||||
sources.append("#define IN_OUT out\n");
|
||||
if (geomcode) {
|
||||
sources.append("#define USE_GEOMETRY_SHADER\n");
|
||||
}
|
||||
if (defines) {
|
||||
sources.append(defines);
|
||||
}
|
||||
sources.append(vertcode);
|
||||
|
||||
shader->vertex_shader_from_glsl(sources);
|
||||
}
|
||||
|
||||
if (fragcode) {
|
||||
Vector<const char *> sources;
|
||||
standard_defines(sources);
|
||||
sources.append("#define GPU_FRAGMENT_SHADER\n");
|
||||
sources.append("#define IN_OUT in\n");
|
||||
if (geomcode) {
|
||||
sources.append("#define USE_GEOMETRY_SHADER\n");
|
||||
}
|
||||
if (defines) {
|
||||
sources.append(defines);
|
||||
}
|
||||
if (libcode) {
|
||||
sources.append(libcode);
|
||||
}
|
||||
sources.append(fragcode);
|
||||
|
||||
shader->fragment_shader_from_glsl(sources);
|
||||
}
|
||||
|
||||
if (geomcode) {
|
||||
Vector<const char *> sources;
|
||||
standard_defines(sources);
|
||||
sources.append("#define GPU_GEOMETRY_SHADER\n");
|
||||
if (defines) {
|
||||
sources.append(defines);
|
||||
}
|
||||
sources.append(geomcode);
|
||||
|
||||
shader->geometry_shader_from_glsl(sources);
|
||||
}
|
||||
|
||||
if (tf_names != NULL && tf_count > 0) {
|
||||
BLI_assert(tf_type != GPU_SHADER_TFB_NONE);
|
||||
shader->transform_feedback_names_set(Span<const char *>(tf_names, tf_count), tf_type);
|
||||
}
|
||||
|
||||
if (!shader->finalize()) {
|
||||
delete shader;
|
||||
return NULL;
|
||||
};
|
||||
|
||||
return static_cast<GPUShader *>(shader);
|
||||
}
|
||||
|
||||
void GPU_shader_free(GPUShader *shader)
|
||||
{
|
||||
delete static_cast<Shader *>(shader);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation utils
|
||||
* \{ */
|
||||
|
||||
GPUShader *GPU_shader_create(const char *vertcode,
|
||||
const char *fragcode,
|
||||
const char *geocode,
|
||||
const char *geomcode,
|
||||
const char *libcode,
|
||||
const char *defines,
|
||||
const char *shname)
|
||||
{
|
||||
return GPU_shader_create_ex(
|
||||
vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname);
|
||||
vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname);
|
||||
}
|
||||
|
||||
GPUShader *GPU_shader_create_from_python(const char *vertexcode,
|
||||
GPUShader *GPU_shader_create_from_python(const char *vertcode,
|
||||
const char *fragcode,
|
||||
const char *geocode,
|
||||
const char *geomcode,
|
||||
const char *libcode,
|
||||
const char *defines)
|
||||
{
|
||||
|
@ -253,241 +245,12 @@ GPUShader *GPU_shader_create_from_python(const char *vertexcode,
|
|||
}
|
||||
|
||||
GPUShader *sh = GPU_shader_create_ex(
|
||||
vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL);
|
||||
vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL);
|
||||
|
||||
MEM_SAFE_FREE(libcodecat);
|
||||
return sh;
|
||||
}
|
||||
|
||||
GPUShader *GPU_shader_create_ex(const char *vertexcode,
|
||||
const char *fragcode,
|
||||
const char *geocode,
|
||||
const char *libcode,
|
||||
const char *defines,
|
||||
const eGPUShaderTFBType tf_type,
|
||||
const char **tf_names,
|
||||
const int tf_count,
|
||||
const char *shname)
|
||||
{
|
||||
GLint status;
|
||||
GLchar log[5000];
|
||||
GLsizei length = 0;
|
||||
GPUShader *shader;
|
||||
char standard_defines[MAX_DEFINE_LENGTH] = "";
|
||||
char standard_extensions[MAX_EXT_DEFINE_LENGTH] = "";
|
||||
|
||||
shader = (GPUShader *)MEM_callocN(sizeof(GPUShader), "GPUShader");
|
||||
gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE);
|
||||
|
||||
#ifndef NDEBUG
|
||||
BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++);
|
||||
#else
|
||||
UNUSED_VARS(shname);
|
||||
#endif
|
||||
|
||||
/* At least a vertex shader and a fragment shader are required. */
|
||||
BLI_assert((fragcode != NULL) && (vertexcode != NULL));
|
||||
|
||||
if (vertexcode) {
|
||||
shader->vertex = glCreateShader(GL_VERTEX_SHADER);
|
||||
}
|
||||
if (fragcode) {
|
||||
shader->fragment = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
}
|
||||
if (geocode) {
|
||||
shader->geometry = glCreateShader(GL_GEOMETRY_SHADER);
|
||||
}
|
||||
|
||||
shader->program = glCreateProgram();
|
||||
|
||||
if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) ||
|
||||
(geocode && !shader->geometry)) {
|
||||
fprintf(stderr, "GPUShader, object creation failed.\n");
|
||||
GPU_shader_free(shader);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gpu_shader_standard_defines(standard_defines);
|
||||
gpu_shader_standard_extensions(standard_extensions);
|
||||
|
||||
if (vertexcode) {
|
||||
const char *source[7];
|
||||
/* custom limit, may be too small, beware */
|
||||
int num_source = 0;
|
||||
|
||||
source[num_source++] = gpu_shader_version();
|
||||
source[num_source++] =
|
||||
"#define GPU_VERTEX_SHADER\n"
|
||||
"#define IN_OUT out\n";
|
||||
source[num_source++] = standard_extensions;
|
||||
source[num_source++] = standard_defines;
|
||||
|
||||
if (geocode) {
|
||||
source[num_source++] = "#define USE_GEOMETRY_SHADER\n";
|
||||
}
|
||||
if (defines) {
|
||||
source[num_source++] = defines;
|
||||
}
|
||||
source[num_source++] = vertexcode;
|
||||
|
||||
gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX);
|
||||
|
||||
glAttachShader(shader->program, shader->vertex);
|
||||
glShaderSource(shader->vertex, num_source, source, NULL);
|
||||
|
||||
glCompileShader(shader->vertex);
|
||||
glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status);
|
||||
|
||||
if (!status) {
|
||||
glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log);
|
||||
shader_print_errors("compile", log, source, num_source);
|
||||
|
||||
GPU_shader_free(shader);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (fragcode) {
|
||||
const char *source[8];
|
||||
int num_source = 0;
|
||||
|
||||
source[num_source++] = gpu_shader_version();
|
||||
source[num_source++] =
|
||||
"#define GPU_FRAGMENT_SHADER\n"
|
||||
"#define IN_OUT in\n";
|
||||
source[num_source++] = standard_extensions;
|
||||
source[num_source++] = standard_defines;
|
||||
|
||||
if (geocode) {
|
||||
source[num_source++] = "#define USE_GEOMETRY_SHADER\n";
|
||||
}
|
||||
if (defines) {
|
||||
source[num_source++] = defines;
|
||||
}
|
||||
if (libcode) {
|
||||
source[num_source++] = libcode;
|
||||
}
|
||||
source[num_source++] = fragcode;
|
||||
|
||||
gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT);
|
||||
|
||||
glAttachShader(shader->program, shader->fragment);
|
||||
glShaderSource(shader->fragment, num_source, source, NULL);
|
||||
|
||||
glCompileShader(shader->fragment);
|
||||
glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status);
|
||||
|
||||
if (!status) {
|
||||
glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log);
|
||||
shader_print_errors("compile", log, source, num_source);
|
||||
|
||||
GPU_shader_free(shader);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (geocode) {
|
||||
const char *source[6];
|
||||
int num_source = 0;
|
||||
|
||||
source[num_source++] = gpu_shader_version();
|
||||
source[num_source++] = "#define GPU_GEOMETRY_SHADER\n";
|
||||
source[num_source++] = standard_extensions;
|
||||
source[num_source++] = standard_defines;
|
||||
|
||||
if (defines) {
|
||||
source[num_source++] = defines;
|
||||
}
|
||||
source[num_source++] = geocode;
|
||||
|
||||
gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY);
|
||||
|
||||
glAttachShader(shader->program, shader->geometry);
|
||||
glShaderSource(shader->geometry, num_source, source, NULL);
|
||||
|
||||
glCompileShader(shader->geometry);
|
||||
glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status);
|
||||
|
||||
if (!status) {
|
||||
glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log);
|
||||
shader_print_errors("compile", log, source, num_source);
|
||||
|
||||
GPU_shader_free(shader);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (tf_names != NULL) {
|
||||
glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS);
|
||||
/* Primitive type must be setup */
|
||||
BLI_assert(tf_type != GPU_SHADER_TFB_NONE);
|
||||
shader->feedback_transform_type = tf_type;
|
||||
}
|
||||
|
||||
glLinkProgram(shader->program);
|
||||
glGetProgramiv(shader->program, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
glGetProgramInfoLog(shader->program, sizeof(log), &length, log);
|
||||
/* print attached shaders in pipeline order */
|
||||
if (defines) {
|
||||
shader_print_errors("linking", log, &defines, 1);
|
||||
}
|
||||
if (vertexcode) {
|
||||
shader_print_errors("linking", log, &vertexcode, 1);
|
||||
}
|
||||
if (geocode) {
|
||||
shader_print_errors("linking", log, &geocode, 1);
|
||||
}
|
||||
if (libcode) {
|
||||
shader_print_errors("linking", log, &libcode, 1);
|
||||
}
|
||||
if (fragcode) {
|
||||
shader_print_errors("linking", log, &fragcode, 1);
|
||||
}
|
||||
|
||||
GPU_shader_free(shader);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
glUseProgram(shader->program);
|
||||
shader->interface = GPU_shaderinterface_create(shader->program);
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
#undef DEBUG_SHADER_GEOMETRY
|
||||
#undef DEBUG_SHADER_FRAGMENT
|
||||
#undef DEBUG_SHADER_VERTEX
|
||||
#undef DEBUG_SHADER_NONE
|
||||
|
||||
void GPU_shader_free(GPUShader *shader)
|
||||
{
|
||||
#if 0 /* Would be nice to have, but for now the Deferred compilation \
|
||||
* does not have a GPUContext. */
|
||||
BLI_assert(GPU_context_active_get() != NULL);
|
||||
#endif
|
||||
BLI_assert(shader);
|
||||
|
||||
if (shader->vertex) {
|
||||
glDeleteShader(shader->vertex);
|
||||
}
|
||||
if (shader->geometry) {
|
||||
glDeleteShader(shader->geometry);
|
||||
}
|
||||
if (shader->fragment) {
|
||||
glDeleteShader(shader->fragment);
|
||||
}
|
||||
if (shader->program) {
|
||||
glDeleteProgram(shader->program);
|
||||
}
|
||||
|
||||
if (shader->interface) {
|
||||
GPU_shaderinterface_discard(shader->interface);
|
||||
}
|
||||
|
||||
MEM_freeN(shader);
|
||||
}
|
||||
|
||||
static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc)
|
||||
{
|
||||
bool is_alloc = false;
|
||||
|
@ -563,21 +326,21 @@ struct GPUShader *GPU_shader_create_from_arrays_impl(
|
|||
/** \name Binding
|
||||
* \{ */
|
||||
|
||||
void GPU_shader_bind(GPUShader *shader)
|
||||
void GPU_shader_bind(GPUShader *gpu_shader)
|
||||
{
|
||||
BLI_assert(shader && shader->program);
|
||||
Shader *shader = static_cast<Shader *>(gpu_shader);
|
||||
|
||||
GPUContext *ctx = GPU_context_active_get();
|
||||
|
||||
if (ctx->shader != shader) {
|
||||
ctx->shader = shader;
|
||||
glUseProgram(shader->program);
|
||||
GPU_matrix_bind(shader->interface);
|
||||
GPU_shader_set_srgb_uniform(shader->interface);
|
||||
shader->bind();
|
||||
GPU_matrix_bind(shader);
|
||||
GPU_shader_set_srgb_uniform(shader);
|
||||
}
|
||||
|
||||
if (GPU_matrix_dirty_get()) {
|
||||
GPU_matrix_bind(shader->interface);
|
||||
GPU_matrix_bind(shader);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -585,8 +348,10 @@ void GPU_shader_unbind(void)
|
|||
{
|
||||
#ifndef NDEBUG
|
||||
GPUContext *ctx = GPU_context_active_get();
|
||||
if (ctx->shader) {
|
||||
static_cast<Shader *>(ctx->shader)->unbind();
|
||||
}
|
||||
ctx->shader = NULL;
|
||||
glUseProgram(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -594,34 +359,18 @@ void GPU_shader_unbind(void)
|
|||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform feedback
|
||||
*
|
||||
* TODO(fclem) Should be replaced by compute shaders.
|
||||
* \{ */
|
||||
|
||||
bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id)
|
||||
bool GPU_shader_transform_feedback_enable(GPUShader *shader, GPUVertBuf *vertbuf)
|
||||
{
|
||||
if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id);
|
||||
|
||||
switch (shader->feedback_transform_type) {
|
||||
case GPU_SHADER_TFB_POINTS:
|
||||
glBeginTransformFeedback(GL_POINTS);
|
||||
return true;
|
||||
case GPU_SHADER_TFB_LINES:
|
||||
glBeginTransformFeedback(GL_LINES);
|
||||
return true;
|
||||
case GPU_SHADER_TFB_TRIANGLES:
|
||||
glBeginTransformFeedback(GL_TRIANGLES);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return static_cast<Shader *>(shader)->transform_feedback_enable(vertbuf);
|
||||
}
|
||||
|
||||
void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader))
|
||||
void GPU_shader_transform_feedback_disable(GPUShader *shader)
|
||||
{
|
||||
glEndTransformFeedback();
|
||||
static_cast<Shader *>(shader)->transform_feedback_disable();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -632,49 +381,42 @@ void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader))
|
|||
|
||||
int GPU_shader_get_uniform(GPUShader *shader, const char *name)
|
||||
{
|
||||
BLI_assert(shader && shader->program);
|
||||
const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name);
|
||||
return uniform ? uniform->location : -1;
|
||||
}
|
||||
|
||||
int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin)
|
||||
{
|
||||
BLI_assert(shader && shader->program);
|
||||
return GPU_shaderinterface_uniform_builtin(shader->interface,
|
||||
static_cast<GPUUniformBuiltin>(builtin));
|
||||
}
|
||||
|
||||
int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
|
||||
{
|
||||
BLI_assert(shader && shader->program);
|
||||
return GPU_shaderinterface_block_builtin(shader->interface,
|
||||
static_cast<GPUUniformBlockBuiltin>(builtin));
|
||||
}
|
||||
|
||||
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name)
|
||||
{
|
||||
BLI_assert(shader && shader->program);
|
||||
const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name);
|
||||
return ubo ? ubo->location : -1;
|
||||
}
|
||||
|
||||
int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name)
|
||||
{
|
||||
BLI_assert(shader && shader->program);
|
||||
const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name);
|
||||
return ubo ? ubo->binding : -1;
|
||||
}
|
||||
|
||||
int GPU_shader_get_texture_binding(GPUShader *shader, const char *name)
|
||||
{
|
||||
BLI_assert(shader && shader->program);
|
||||
const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name);
|
||||
return tex ? tex->binding : -1;
|
||||
}
|
||||
|
||||
int GPU_shader_get_attribute(GPUShader *shader, const char *name)
|
||||
{
|
||||
BLI_assert(shader && shader->program);
|
||||
const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name);
|
||||
return attr ? attr->location : -1;
|
||||
}
|
||||
|
@ -686,9 +428,10 @@ int GPU_shader_get_attribute(GPUShader *shader, const char *name)
|
|||
* \{ */
|
||||
|
||||
/* Clement : Temp */
|
||||
int GPU_shader_get_program(GPUShader *shader)
|
||||
int GPU_shader_get_program(GPUShader *UNUSED(shader))
|
||||
{
|
||||
return (int)shader->program;
|
||||
/* TODO fixme */
|
||||
return (int)0;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -698,57 +441,15 @@ int GPU_shader_get_program(GPUShader *shader)
|
|||
* \{ */
|
||||
|
||||
void GPU_shader_uniform_vector(
|
||||
GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value)
|
||||
GPUShader *shader, int loc, int len, int arraysize, const float *value)
|
||||
{
|
||||
if (location == -1 || value == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (length) {
|
||||
case 1:
|
||||
glUniform1fv(location, arraysize, value);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2fv(location, arraysize, value);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3fv(location, arraysize, value);
|
||||
break;
|
||||
case 4:
|
||||
glUniform4fv(location, arraysize, value);
|
||||
break;
|
||||
case 9:
|
||||
glUniformMatrix3fv(location, arraysize, 0, value);
|
||||
break;
|
||||
case 16:
|
||||
glUniformMatrix4fv(location, arraysize, 0, value);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
static_cast<Shader *>(shader)->uniform_float(loc, len, arraysize, value);
|
||||
}
|
||||
|
||||
void GPU_shader_uniform_vector_int(
|
||||
GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value)
|
||||
GPUShader *shader, int loc, int len, int arraysize, const int *value)
|
||||
{
|
||||
switch (length) {
|
||||
case 1:
|
||||
glUniform1iv(location, arraysize, value);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2iv(location, arraysize, value);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3iv(location, arraysize, value);
|
||||
break;
|
||||
case 4:
|
||||
glUniform4iv(location, arraysize, value);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
static_cast<Shader *>(shader)->uniform_int(loc, len, arraysize, value);
|
||||
}
|
||||
|
||||
void GPU_shader_uniform_int(GPUShader *shader, int location, int value)
|
||||
|
@ -851,11 +552,11 @@ void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, cons
|
|||
|
||||
static int g_shader_builtin_srgb_transform = 0;
|
||||
|
||||
void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface)
|
||||
void GPU_shader_set_srgb_uniform(GPUShader *shader)
|
||||
{
|
||||
int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM);
|
||||
int32_t loc = GPU_shaderinterface_uniform_builtin(shader->interface, GPU_UNIFORM_SRGB_TRANSFORM);
|
||||
if (loc != -1) {
|
||||
glUniform1i(loc, g_shader_builtin_srgb_transform);
|
||||
GPU_shader_uniform_vector_int(shader, loc, 1, 1, &g_shader_builtin_srgb_transform);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
#include "GPU_texture.h"
|
||||
#include "GPU_uniformbuffer.h"
|
||||
|
||||
#include "gpu_shader_private.h"
|
||||
|
||||
/* Adjust these constants as needed. */
|
||||
#define MAX_DEFINE_LENGTH 256
|
||||
#define MAX_EXT_DEFINE_LENGTH 512
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader_interface.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct GPUShader {
|
||||
/** Handle for full program (links shader stages below). */
|
||||
GLuint program;
|
||||
|
||||
/** Handle for vertex shader. */
|
||||
GLuint vertex;
|
||||
/** Handle for geometry shader. */
|
||||
GLuint geometry;
|
||||
/** Handle for fragment shader. */
|
||||
GLuint fragment;
|
||||
|
||||
/** Cached uniform & attribute interface for shader. */
|
||||
GPUShaderInterface *interface;
|
||||
|
||||
int feedback_transform_type;
|
||||
#ifndef NDEBUG
|
||||
char name[64];
|
||||
#endif
|
||||
};
|
||||
|
||||
/* XXX do not use it. Special hack to use OCIO with batch API. */
|
||||
GPUShader *immGetShader(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_shader_interface.h"
|
||||
#include "GPU_vertex_buffer.h"
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
|
||||
class Shader : public GPUShader {
|
||||
public:
|
||||
Shader(const char *name);
|
||||
virtual ~Shader();
|
||||
|
||||
virtual void vertex_shader_from_glsl(MutableSpan<const char *> sources) = 0;
|
||||
virtual void geometry_shader_from_glsl(MutableSpan<const char *> sources) = 0;
|
||||
virtual void fragment_shader_from_glsl(MutableSpan<const char *> sources) = 0;
|
||||
virtual bool finalize(void) = 0;
|
||||
|
||||
virtual void transform_feedback_names_set(Span<const char *> name_list,
|
||||
const eGPUShaderTFBType geom_type) = 0;
|
||||
virtual bool transform_feedback_enable(GPUVertBuf *) = 0;
|
||||
virtual void transform_feedback_disable(void) = 0;
|
||||
|
||||
virtual void bind(void) = 0;
|
||||
virtual void unbind(void) = 0;
|
||||
|
||||
virtual void uniform_float(int location, int comp_len, int array_size, const float *data) = 0;
|
||||
virtual void uniform_int(int location, int comp_len, int array_size, const int *data) = 0;
|
||||
|
||||
protected:
|
||||
void print_errors(Span<const char *> sources, const char *log);
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
||||
|
||||
/* XXX do not use it. Special hack to use OCIO with batch API. */
|
||||
GPUShader *immGetShader(void);
|
|
@ -33,7 +33,6 @@
|
|||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "gpu_shader_private.h"
|
||||
|
||||
#define PACK_DEBUG 0
|
||||
|
||||
|
@ -480,6 +479,8 @@ static void get_fetch_mode_and_comp_type(int gl_type,
|
|||
|
||||
void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader)
|
||||
{
|
||||
UNUSED_VARS(format, shader);
|
||||
#if 0 /* TODO (fclem) port to GLShader */
|
||||
GPU_vertformat_clear(format);
|
||||
GPUVertAttr *attr = &format->attrs[0];
|
||||
|
||||
|
@ -512,4 +513,5 @@ void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader)
|
|||
attr->comp_type = comp_type;
|
||||
attr += 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "gl_batch.hh"
|
||||
#include "gl_context.hh"
|
||||
#include "gl_drawlist.hh"
|
||||
#include "gl_shader.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
|
@ -54,6 +55,11 @@ class GLBackend : public GPUBackend {
|
|||
return new GLDrawList(list_length);
|
||||
};
|
||||
|
||||
Shader *shader_alloc(const char *name)
|
||||
{
|
||||
return new GLShader(name);
|
||||
};
|
||||
|
||||
/* TODO remove */
|
||||
void buf_free(GLuint buf_id);
|
||||
void tex_free(GLuint tex_id);
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
#include "gpu_batch_private.hh"
|
||||
#include "gpu_primitive_private.h"
|
||||
#include "gpu_shader_private.h"
|
||||
|
||||
#include "gl_batch.hh"
|
||||
#include "gl_context.hh"
|
||||
|
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "GPU_extensions.h"
|
||||
#include "GPU_platform.h"
|
||||
|
||||
#include "gl_shader.hh"
|
||||
|
||||
using namespace blender;
|
||||
using namespace blender::gpu;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Creation / Destruction
|
||||
* \{ */
|
||||
|
||||
GLShader::GLShader(const char *name) : Shader(name)
|
||||
{
|
||||
#if 0 /* Would be nice to have, but for now the Deferred compilation \
|
||||
* does not have a GPUContext. */
|
||||
BLI_assert(GPU_context_active_get() != NULL);
|
||||
#endif
|
||||
shader_program_ = glCreateProgram();
|
||||
}
|
||||
|
||||
GLShader::~GLShader(void)
|
||||
{
|
||||
#if 0 /* Would be nice to have, but for now the Deferred compilation \
|
||||
* does not have a GPUContext. */
|
||||
BLI_assert(GPU_context_active_get() != NULL);
|
||||
#endif
|
||||
/* Invalid handles are silently ignored. */
|
||||
glDeleteShader(vert_shader_);
|
||||
glDeleteShader(geom_shader_);
|
||||
glDeleteShader(frag_shader_);
|
||||
glDeleteProgram(shader_program_);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Shader stage creation
|
||||
* \{ */
|
||||
|
||||
char *GLShader::glsl_patch_get(void)
|
||||
{
|
||||
/** Used for shader patching. Init once. */
|
||||
static char patch[512] = "\0";
|
||||
if (patch[0] != '\0') {
|
||||
return patch;
|
||||
}
|
||||
|
||||
size_t slen = 0;
|
||||
/* Version need to go first. */
|
||||
STR_CONCAT(patch, slen, "#version 330\n");
|
||||
|
||||
/* Enable extensions for features that are not part of our base GLSL version
|
||||
* don't use an extension for something already available! */
|
||||
if (GLEW_ARB_texture_gather) {
|
||||
/* There is a bug on older Nvidia GPU where GL_ARB_texture_gather
|
||||
* is reported to be supported but yield a compile error (see T55802). */
|
||||
if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) {
|
||||
STR_CONCAT(patch, slen, "#extension GL_ARB_texture_gather: enable\n");
|
||||
|
||||
/* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the
|
||||
* shader so double check the preprocessor define (see T56544). */
|
||||
if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) {
|
||||
STR_CONCAT(patch, slen, "#ifdef GL_ARB_texture_gather\n");
|
||||
STR_CONCAT(patch, slen, "# define GPU_ARB_texture_gather\n");
|
||||
STR_CONCAT(patch, slen, "#endif\n");
|
||||
}
|
||||
else {
|
||||
STR_CONCAT(patch, slen, "#define GPU_ARB_texture_gather\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GLEW_ARB_shader_draw_parameters) {
|
||||
STR_CONCAT(patch, slen, "#extension GL_ARB_shader_draw_parameters : enable\n");
|
||||
STR_CONCAT(patch, slen, "#define GPU_ARB_shader_draw_parameters\n");
|
||||
}
|
||||
if (GPU_arb_texture_cube_map_array_is_supported()) {
|
||||
STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n");
|
||||
STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n");
|
||||
}
|
||||
|
||||
/* Derivative sign can change depending on implementation. */
|
||||
float derivatives[2];
|
||||
GPU_get_dfdy_factors(derivatives);
|
||||
STR_CONCATF(patch, slen, "#define DFDX_SIGN %1.1f\n", derivatives[0]);
|
||||
STR_CONCATF(patch, slen, "#define DFDY_SIGN %1.1f\n", derivatives[1]);
|
||||
|
||||
BLI_assert(slen < sizeof(patch));
|
||||
return patch;
|
||||
}
|
||||
|
||||
/* Create, compile and attach the shader stage to the shader program. */
|
||||
GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources)
|
||||
{
|
||||
GLuint shader = glCreateShader(gl_stage);
|
||||
if (shader == 0) {
|
||||
fprintf(stderr, "GLShader: Error: Could not create shader object.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Patch the shader code using the first source slot. */
|
||||
sources[0] = glsl_patch_get();
|
||||
|
||||
glShaderSource(shader, sources.size(), sources.data(), NULL);
|
||||
glCompileShader(shader);
|
||||
|
||||
GLint status;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (!status) {
|
||||
char log[5000];
|
||||
glGetShaderInfoLog(shader, sizeof(log), NULL, log);
|
||||
this->print_errors(sources, log);
|
||||
glDeleteShader(shader);
|
||||
return 0;
|
||||
}
|
||||
glAttachShader(shader_program_, shader);
|
||||
return shader;
|
||||
}
|
||||
|
||||
void GLShader::vertex_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
{
|
||||
vert_shader_ = this->create_shader_stage(GL_VERTEX_SHADER, sources);
|
||||
}
|
||||
|
||||
void GLShader::geometry_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
{
|
||||
geom_shader_ = this->create_shader_stage(GL_GEOMETRY_SHADER, sources);
|
||||
}
|
||||
|
||||
void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
{
|
||||
frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources);
|
||||
}
|
||||
|
||||
bool GLShader::finalize(void)
|
||||
{
|
||||
glLinkProgram(shader_program_);
|
||||
|
||||
GLint status;
|
||||
glGetProgramiv(shader_program_, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
char log[5000];
|
||||
glGetProgramInfoLog(shader_program_, sizeof(log), NULL, log);
|
||||
fprintf(stderr, "\nLinking Error:\n\n%s", log);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO(fclem) We need this to modify the image binding points using glUniform.
|
||||
* This could be avoided using glProgramUniform in GL 4.1. */
|
||||
glUseProgram(shader_program_);
|
||||
interface = GPU_shaderinterface_create(shader_program_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Binding
|
||||
* \{ */
|
||||
|
||||
void GLShader::bind(void)
|
||||
{
|
||||
BLI_assert(shader_program_ != 0);
|
||||
glUseProgram(shader_program_);
|
||||
}
|
||||
|
||||
void GLShader::unbind(void)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
glUseProgram(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Transform feedback
|
||||
*
|
||||
* TODO(fclem) Should be replaced by compute shaders.
|
||||
* \{ */
|
||||
|
||||
/* Should be called before linking. */
|
||||
void GLShader::transform_feedback_names_set(Span<const char *> name_list,
|
||||
const eGPUShaderTFBType geom_type)
|
||||
{
|
||||
glTransformFeedbackVaryings(
|
||||
shader_program_, name_list.size(), name_list.data(), GL_INTERLEAVED_ATTRIBS);
|
||||
transform_feedback_type_ = geom_type;
|
||||
}
|
||||
|
||||
bool GLShader::transform_feedback_enable(GPUVertBuf *buf)
|
||||
{
|
||||
if (transform_feedback_type_ == GPU_SHADER_TFB_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_assert(buf->vbo_id != 0);
|
||||
|
||||
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf->vbo_id);
|
||||
|
||||
switch (transform_feedback_type_) {
|
||||
case GPU_SHADER_TFB_POINTS:
|
||||
glBeginTransformFeedback(GL_POINTS);
|
||||
break;
|
||||
case GPU_SHADER_TFB_LINES:
|
||||
glBeginTransformFeedback(GL_LINES);
|
||||
break;
|
||||
case GPU_SHADER_TFB_TRIANGLES:
|
||||
glBeginTransformFeedback(GL_TRIANGLES);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLShader::transform_feedback_disable(void)
|
||||
{
|
||||
glEndTransformFeedback();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Uniforms setters
|
||||
* \{ */
|
||||
|
||||
void GLShader::uniform_float(int location, int comp_len, int array_size, const float *data)
|
||||
{
|
||||
switch (comp_len) {
|
||||
case 1:
|
||||
glUniform1fv(location, array_size, data);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2fv(location, array_size, data);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3fv(location, array_size, data);
|
||||
break;
|
||||
case 4:
|
||||
glUniform4fv(location, array_size, data);
|
||||
break;
|
||||
case 9:
|
||||
glUniformMatrix3fv(location, array_size, 0, data);
|
||||
break;
|
||||
case 16:
|
||||
glUniformMatrix4fv(location, array_size, 0, data);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GLShader::uniform_int(int location, int comp_len, int array_size, const int *data)
|
||||
{
|
||||
switch (comp_len) {
|
||||
case 1:
|
||||
glUniform1iv(location, array_size, data);
|
||||
break;
|
||||
case 2:
|
||||
glUniform2iv(location, array_size, data);
|
||||
break;
|
||||
case 3:
|
||||
glUniform3iv(location, array_size, data);
|
||||
break;
|
||||
case 4:
|
||||
glUniform4iv(location, array_size, data);
|
||||
break;
|
||||
default:
|
||||
BLI_assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2020 Blender Foundation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*
|
||||
* GPUDrawList is an API to do lots of similar draw-calls very fast using
|
||||
* multi-draw-indirect. There is a fallback if the feature is not supported.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "glew-mx.h"
|
||||
|
||||
#include "gpu_shader_private.hh"
|
||||
|
||||
namespace blender {
|
||||
namespace gpu {
|
||||
|
||||
class GLShader : public Shader {
|
||||
private:
|
||||
/** Handle for full program (links shader stages below). */
|
||||
GLuint shader_program_ = 0;
|
||||
|
||||
GLuint vert_shader_ = 0;
|
||||
GLuint geom_shader_ = 0;
|
||||
GLuint frag_shader_ = 0;
|
||||
|
||||
eGPUShaderTFBType transform_feedback_type_ = GPU_SHADER_TFB_NONE;
|
||||
|
||||
public:
|
||||
GLShader(const char *name);
|
||||
~GLShader();
|
||||
|
||||
/* Return true on success. */
|
||||
void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
void fragment_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
bool finalize(void) override;
|
||||
|
||||
void transform_feedback_names_set(Span<const char *> name_list,
|
||||
const eGPUShaderTFBType geom_type);
|
||||
bool transform_feedback_enable(GPUVertBuf *buf) override;
|
||||
void transform_feedback_disable(void) override;
|
||||
|
||||
void bind(void) override;
|
||||
void unbind(void) override;
|
||||
|
||||
void uniform_float(int location, int comp_len, int array_size, const float *data) override;
|
||||
void uniform_int(int location, int comp_len, int array_size, const int *data) override;
|
||||
|
||||
private:
|
||||
char *glsl_patch_get(void);
|
||||
|
||||
GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources);
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("GLShader");
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace blender
|
|
@ -604,7 +604,8 @@ PyDoc_STRVAR(
|
|||
" ``GPU_ATI``, ``GPU_NVIDIA`` and ``GPU_INTEL``.\n"
|
||||
"\n"
|
||||
" The following extensions are enabled by default if supported by the GPU:\n"
|
||||
" ``GL_ARB_texture_gather`` and ``GL_ARB_texture_query_lod``.\n"
|
||||
" ``GL_ARB_texture_gather``, ``GL_ARB_texture_cube_map_array`` and "
|
||||
"``GL_ARB_shader_draw_parameters``.\n"
|
||||
"\n"
|
||||
" To debug shaders, use the --debug-gpu-shaders command line option"
|
||||
" to see full GLSL shader compilation and linking errors.\n"
|
||||
|
|
Loading…
Reference in New Issue