summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Lutz <michi@icosahedron.de>2021-01-16 16:43:20 +0100
committerMichael Lutz <michi@icosahedron.de>2021-02-22 22:16:07 +0100
commitacf59f6b68a3bdd068b4eddccd06749c5b411189 (patch)
treeb1df72248100526f0f9ca24df283b633718ff168
parentb1818596294346a87bcb2ddb23d0b471c5c31284 (diff)
downloadopenttd-acf59f6b68a3bdd068b4eddccd06749c5b411189.tar.xz
Codechange: [OpenGL] Use shaders to display the video buffer on screen.
-rw-r--r--src/table/CMakeLists.txt5
-rw-r--r--src/table/opengl_shader.h28
-rw-r--r--src/video/opengl.cpp157
-rw-r--r--src/video/opengl.h2
4 files changed, 191 insertions, 1 deletions
diff --git a/src/table/CMakeLists.txt b/src/table/CMakeLists.txt
index e63337b8a..bb2311cf1 100644
--- a/src/table/CMakeLists.txt
+++ b/src/table/CMakeLists.txt
@@ -85,3 +85,8 @@ add_files(
unicode.h
water_land.h
)
+
+add_files(
+ opengl_shader.h
+ CONDITION OPENGL_FOUND
+)
diff --git a/src/table/opengl_shader.h b/src/table/opengl_shader.h
new file mode 100644
index 000000000..43b2ca512
--- /dev/null
+++ b/src/table/opengl_shader.h
@@ -0,0 +1,28 @@
+/* $Id$ */
+
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD 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, version 2.
+ * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** @file opengl_shader.h OpenGL shader programs. */
+
+/** Vertex shader that just passes colour and tex coords through. */
+static const char *_vertex_shader_direct[] = {
+ "#version 110\n",
+ "void main() {",
+ " gl_TexCoord[0] = gl_MultiTexCoord0;",
+ " gl_Position = gl_Vertex;",
+ "}",
+};
+
+/** Fragment shader that reads the fragment colour from a 32bpp texture. */
+static const char *_frag_shader_direct[] = {
+ "#version 110\n",
+ "uniform sampler2D colour_tex;",
+ "void main() {",
+ " gl_FragColor = texture2D(colour_tex, gl_TexCoord[0].st);",
+ "}",
+};
diff --git a/src/video/opengl.cpp b/src/video/opengl.cpp
index e128b77f3..dbf196164 100644
--- a/src/video/opengl.cpp
+++ b/src/video/opengl.cpp
@@ -28,6 +28,9 @@
#include "../gfx_func.h"
#include "../debug.h"
+#include "../table/opengl_shader.h"
+
+
#include "../safeguards.h"
@@ -45,6 +48,22 @@ static PFNGLGENVERTEXARRAYSPROC _glGenVertexArrays;
static PFNGLDELETEVERTEXARRAYSPROC _glDeleteVertexArrays;
static PFNGLBINDVERTEXARRAYPROC _glBindVertexArray;
+static PFNGLCREATEPROGRAMPROC _glCreateProgram;
+static PFNGLDELETEPROGRAMPROC _glDeleteProgram;
+static PFNGLLINKPROGRAMPROC _glLinkProgram;
+static PFNGLUSEPROGRAMPROC _glUseProgram;
+static PFNGLGETPROGRAMIVPROC _glGetProgramiv;
+static PFNGLGETPROGRAMINFOLOGPROC _glGetProgramInfoLog;
+static PFNGLCREATESHADERPROC _glCreateShader;
+static PFNGLDELETESHADERPROC _glDeleteShader;
+static PFNGLSHADERSOURCEPROC _glShaderSource;
+static PFNGLCOMPILESHADERPROC _glCompileShader;
+static PFNGLATTACHSHADERPROC _glAttachShader;
+static PFNGLGETSHADERIVPROC _glGetShaderiv;
+static PFNGLGETSHADERINFOLOGPROC _glGetShaderInfoLog;
+static PFNGLGETUNIFORMLOCATIONPROC _glGetUniformLocation;
+static PFNGLUNIFORM1IPROC _glUniform1i;
+
/** A simple 2D vertex with just position and texture. */
struct Simple2DVertex {
float x, y;
@@ -171,6 +190,49 @@ static bool BindVBAExtension()
return _glGenVertexArrays != nullptr && _glDeleteVertexArrays != nullptr && _glBindVertexArray != nullptr;
}
+/** Bind extension functions for shader support. */
+static bool BindShaderExtensions()
+{
+ if (IsOpenGLVersionAtLeast(2, 0)) {
+ _glCreateProgram = (PFNGLCREATEPROGRAMPROC)GetOGLProcAddress("glCreateProgram");
+ _glDeleteProgram = (PFNGLDELETEPROGRAMPROC)GetOGLProcAddress("glDeleteProgram");
+ _glLinkProgram = (PFNGLLINKPROGRAMPROC)GetOGLProcAddress("glLinkProgram");
+ _glUseProgram = (PFNGLUSEPROGRAMPROC)GetOGLProcAddress("glUseProgram");
+ _glGetProgramiv = (PFNGLGETPROGRAMIVPROC)GetOGLProcAddress("glGetProgramiv");
+ _glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)GetOGLProcAddress("glGetProgramInfoLog");
+ _glCreateShader = (PFNGLCREATESHADERPROC)GetOGLProcAddress("glCreateShader");
+ _glDeleteShader = (PFNGLDELETESHADERPROC)GetOGLProcAddress("glDeleteShader");
+ _glShaderSource = (PFNGLSHADERSOURCEPROC)GetOGLProcAddress("glShaderSource");
+ _glCompileShader = (PFNGLCOMPILESHADERPROC)GetOGLProcAddress("glCompileShader");
+ _glAttachShader = (PFNGLATTACHSHADERPROC)GetOGLProcAddress("glAttachShader");
+ _glGetShaderiv = (PFNGLGETSHADERIVPROC)GetOGLProcAddress("glGetShaderiv");
+ _glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)GetOGLProcAddress("glGetShaderInfoLog");
+ _glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)GetOGLProcAddress("glGetUniformLocation");
+ _glUniform1i = (PFNGLUNIFORM1IPROC)GetOGLProcAddress("glUniform1i");
+ } else {
+ /* In the ARB extension programs and shaders are in the same object space. */
+ _glCreateProgram = (PFNGLCREATEPROGRAMPROC)GetOGLProcAddress("glCreateProgramObjectARB");
+ _glDeleteProgram = (PFNGLDELETEPROGRAMPROC)GetOGLProcAddress("glDeleteObjectARB");
+ _glLinkProgram = (PFNGLLINKPROGRAMPROC)GetOGLProcAddress("glLinkProgramARB");
+ _glUseProgram = (PFNGLUSEPROGRAMPROC)GetOGLProcAddress("glUseProgramObjectARB");
+ _glGetProgramiv = (PFNGLGETPROGRAMIVPROC)GetOGLProcAddress("glGetObjectParameterivARB");
+ _glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)GetOGLProcAddress("glGetInfoLogARB");
+ _glCreateShader = (PFNGLCREATESHADERPROC)GetOGLProcAddress("glCreateShaderObjectARB");
+ _glDeleteShader = (PFNGLDELETESHADERPROC)GetOGLProcAddress("glDeleteObjectARB");
+ _glShaderSource = (PFNGLSHADERSOURCEPROC)GetOGLProcAddress("glShaderSourceARB");
+ _glCompileShader = (PFNGLCOMPILESHADERPROC)GetOGLProcAddress("glCompileShaderARB");
+ _glAttachShader = (PFNGLATTACHSHADERPROC)GetOGLProcAddress("glAttachObjectARB");
+ _glGetShaderiv = (PFNGLGETSHADERIVPROC)GetOGLProcAddress("glGetObjectParameterivARB");
+ _glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)GetOGLProcAddress("glGetInfoLogARB");
+ _glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)GetOGLProcAddress("glGetUniformLocationARB");
+ _glUniform1i = (PFNGLUNIFORM1IPROC)GetOGLProcAddress("glUniform1iARB");
+ }
+
+ return _glCreateProgram != nullptr && _glDeleteProgram != nullptr && _glLinkProgram != nullptr && _glGetProgramiv != nullptr && _glGetProgramInfoLog != nullptr &&
+ _glCreateShader != nullptr && _glDeleteShader != nullptr && _glShaderSource != nullptr && _glCompileShader != nullptr && _glAttachShader != nullptr &&
+ _glGetShaderiv != nullptr && _glGetShaderInfoLog != nullptr && _glGetUniformLocation != nullptr && _glUniform1i != nullptr;
+}
+
/** Callback to receive OpenGL debug messages. */
void APIENTRY DebugOutputCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
{
@@ -261,6 +323,9 @@ OpenGLBackend::OpenGLBackend()
*/
OpenGLBackend::~OpenGLBackend()
{
+ if (_glDeleteProgram != nullptr) {
+ _glDeleteProgram(this->vid_program);
+ }
if (_glDeleteVertexArrays != nullptr) _glDeleteVertexArrays(1, &this->vao_quad);
if (_glDeleteBuffers != nullptr) {
_glDeleteBuffers(1, &this->vbo_quad);
@@ -302,6 +367,13 @@ const char *OpenGLBackend::Init()
/* Check for vertex array objects. */
if (!IsOpenGLVersionAtLeast(3, 0) && (!IsOpenGLExtensionSupported("GL_ARB_vertex_array_object") || !IsOpenGLExtensionSupported("GL_APPLE_vertex_array_object"))) return "Vertex array objects not supported";
if (!BindVBAExtension()) return "Failed to bind VBA extension functions";
+ /* Check for shader objects. */
+ if (!IsOpenGLVersionAtLeast(2, 0) && (!IsOpenGLExtensionSupported("GL_ARB_shader_objects") || !IsOpenGLExtensionSupported("GL_ARB_fragment_shader") || !IsOpenGLExtensionSupported("GL_ARB_vertex_shader"))) return "No shader support";
+ if (!BindShaderExtensions()) return "Failed to bind shader extension functions";
+
+ DEBUG(driver, 2, "OpenGL shading language version: %s", (const char *)glGetString(GL_SHADING_LANGUAGE_VERSION));
+
+ if (!this->InitShaders()) return "Failed to initialize shaders";
/* Setup video buffer texture. */
glGenTextures(1, &this->vid_texture);
@@ -314,6 +386,11 @@ const char *OpenGLBackend::Init()
glBindTexture(GL_TEXTURE_2D, 0);
if (glGetError() != GL_NO_ERROR) return "Can't generate video buffer texture";
+ /* Bind texture to shader program. */
+ GLint tex_location = _glGetUniformLocation(this->vid_program, "colour_tex");
+ _glUseProgram(this->vid_program);
+ _glUniform1i(tex_location, 0); // Texture unit 0.
+
/* Create pixel buffer object as video buffer storage. */
_glGenBuffers(1, &this->vid_pbo);
_glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vid_pbo);
@@ -338,6 +415,7 @@ const char *OpenGLBackend::Init()
_glBindBuffer(GL_ARRAY_BUFFER, this->vbo_quad);
_glBufferData(GL_ARRAY_BUFFER, sizeof(vert_array), vert_array, GL_STATIC_DRAW);
if (glGetError() != GL_NO_ERROR) return "Can't generate VBO for fullscreen quad";
+
/* Set vertex state. */
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -347,13 +425,89 @@ const char *OpenGLBackend::Init()
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glDisable(GL_DEPTH_TEST);
- glEnable(GL_TEXTURE_2D);
(void)glGetError(); // Clear errors.
return nullptr;
}
/**
+ * Check a shader for compilation errors and log them if necessary.
+ * @param shader Shader to check.
+ * @return True if the shader is valid.
+ */
+static bool VerifyShader(GLuint shader)
+{
+ static ReusableBuffer<char> log_buf;
+
+ GLint result = GL_FALSE;
+ _glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
+
+ /* Output log if there is one. */
+ GLint log_len = 0;
+ _glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len);
+ if (log_len > 0) {
+ _glGetShaderInfoLog(shader, log_len, nullptr, log_buf.Allocate(log_len));
+ DEBUG(driver, result != GL_TRUE ? 0 : 2, "%s", log_buf.GetBuffer()); // Always print on failure.
+ }
+
+ return result == GL_TRUE;
+}
+
+/**
+ * Check a program for link errors and log them if necessary.
+ * @param program Program to check.
+ * @return True if the program is valid.
+ */
+static bool VerifyProgram(GLuint program)
+{
+ static ReusableBuffer<char> log_buf;
+
+ GLint result = GL_FALSE;
+ _glGetProgramiv(program, GL_LINK_STATUS, &result);
+
+ /* Output log if there is one. */
+ GLint log_len = 0;
+ _glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len);
+ if (log_len > 0) {
+ _glGetProgramInfoLog(program, log_len, nullptr, log_buf.Allocate(log_len));
+ DEBUG(driver, result != GL_TRUE ? 0 : 2, "%s", log_buf.GetBuffer()); // Always print on failure.
+ }
+
+ return result == GL_TRUE;
+}
+
+/**
+ * Create all needed shader programs.
+ * @return True if successful, false otherwise.
+ */
+bool OpenGLBackend::InitShaders()
+{
+ /* Create vertex shader. */
+ GLuint vert_shader = _glCreateShader(GL_VERTEX_SHADER);
+ _glShaderSource(vert_shader, lengthof(_vertex_shader_direct), _vertex_shader_direct, nullptr);
+ _glCompileShader(vert_shader);
+ if (!VerifyShader(vert_shader)) return false;
+
+ /* Create fragment shader. */
+ GLuint frag_shader = _glCreateShader(GL_FRAGMENT_SHADER);
+ _glShaderSource(frag_shader, lengthof(_frag_shader_direct), _frag_shader_direct, nullptr);
+ _glCompileShader(frag_shader);
+ if (!VerifyShader(frag_shader)) return false;
+
+ /* Link shaders to program. */
+ this->vid_program = _glCreateProgram();
+ _glAttachShader(this->vid_program, vert_shader);
+ _glAttachShader(this->vid_program, frag_shader);
+ _glLinkProgram(this->vid_program);
+ if (!VerifyProgram(this->vid_program)) return false;
+
+ _glDeleteShader(vert_shader);
+ _glDeleteShader(frag_shader);
+
+ return true;
+}
+
+/**
* Change the size of the drawing window and allocate matching resources.
* @param w New width of the window.
* @param h New height of the window.
@@ -393,6 +547,7 @@ void OpenGLBackend::Paint()
/* Blit video buffer to screen. */
glBindTexture(GL_TEXTURE_2D, this->vid_texture);
+ _glUseProgram(this->vid_program);
_glBindVertexArray(this->vao_quad);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
diff --git a/src/video/opengl.h b/src/video/opengl.h
index 59a30ad65..4a392c280 100644
--- a/src/video/opengl.h
+++ b/src/video/opengl.h
@@ -28,6 +28,7 @@ private:
GLuint vid_pbo; ///< Pixel buffer object storing the memory used for the video driver to draw to.
GLuint vid_texture; ///< Texture handle for the video buffer texture.
+ GLuint vid_program; ///< Shader program for rendering the video buffer.
GLuint vao_quad; ///< Vertex array object storing the rendering state for the fullscreen quad.
GLuint vbo_quad; ///< Vertex buffer with a fullscreen quad.
@@ -35,6 +36,7 @@ private:
~OpenGLBackend();
const char *Init();
+ bool InitShaders();
public:
/** Get singleton instance of this class. */