From 216d78687d2b9468b05fb598d1cef0b8424a40d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Fri, 14 Aug 2020 15:20:35 +0200 Subject: GPUShader: GL backend isolation --- source/blender/gpu/opengl/gl_backend.hh | 6 + source/blender/gpu/opengl/gl_batch.cc | 1 - source/blender/gpu/opengl/gl_shader.cc | 302 ++++++++++++++++++++++++++++++++ source/blender/gpu/opengl/gl_shader.hh | 79 +++++++++ 4 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 source/blender/gpu/opengl/gl_shader.cc create mode 100644 source/blender/gpu/opengl/gl_shader.hh (limited to 'source/blender/gpu/opengl') diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index eba275f0245..63a14af6612 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -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); diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc index 38448e8a0b8..f1cbb2c10d5 100644 --- a/source/blender/gpu/opengl/gl_batch.cc +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -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" diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc new file mode 100644 index 00000000000..ac2490e10ee --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -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 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 sources) +{ + vert_shader_ = this->create_shader_stage(GL_VERTEX_SHADER, sources); +} + +void GLShader::geometry_shader_from_glsl(MutableSpan sources) +{ + geom_shader_ = this->create_shader_stage(GL_GEOMETRY_SHADER, sources); +} + +void GLShader::fragment_shader_from_glsl(MutableSpan 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 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; + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh new file mode 100644 index 00000000000..3bdc9aa80d5 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -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 sources) override; + void geometry_shader_from_glsl(MutableSpan sources) override; + void fragment_shader_from_glsl(MutableSpan sources) override; + bool finalize(void) override; + + void transform_feedback_names_set(Span 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 sources); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLShader"); +}; + +} // namespace gpu +} // namespace blender -- cgit v1.2.3