Mercurial > hg > openttd
changeset 20544:ef2b5b14c3bc draft
-Codechange: [OpenGL] Use shaders to display the video buffer on screen.
author | Michael Lutz <michi@icosahedron.de> |
---|---|
date | Mon, 03 Jun 2013 23:01:11 +0200 |
parents | 9b23c51de4ae |
children | ed076b6fca0b |
files | projects/openttd_vs100.vcxproj projects/openttd_vs100.vcxproj.filters projects/openttd_vs80.vcproj projects/openttd_vs90.vcproj source.list src/table/opengl_shader.h src/video/opengl.cpp src/video/opengl.h |
diffstat | 8 files changed, 197 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -879,6 +879,7 @@ <ClInclude Include="..\src\table\landscape_sprite.h" /> <ClInclude Include="..\src\table\newgrf_debug_data.h" /> <ClInclude Include="..\src\table\object_land.h" /> + <ClInclude Include="..\src\table\opengl_shader.h" /> <ClInclude Include="..\src\table\palette_convert.h" /> <ClInclude Include="..\src\table\palettes.h" /> <ClInclude Include="..\src\table\pricebase.h" />
--- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -1866,6 +1866,9 @@ <ClInclude Include="..\src\table\object_land.h"> <Filter>Tables</Filter> </ClInclude> + <ClInclude Include="..\src\table\opengl_shader.h"> + <Filter>Tables</Filter> + </ClInclude> <ClInclude Include="..\src\table\palette_convert.h"> <Filter>Tables</Filter> </ClInclude>
--- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -2815,6 +2815,10 @@ > </File> <File + RelativePath=".\..\src\table\opengl_shader.h" + > + </File> + <File RelativePath=".\..\src\table\palette_convert.h" > </File>
--- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -2812,6 +2812,10 @@ > </File> <File + RelativePath=".\..\src\table\opengl_shader.h" + > + </File> + <File RelativePath=".\..\src\table\palette_convert.h" > </File>
--- a/source.list +++ b/source.list @@ -641,6 +641,7 @@ table/landscape_sprite.h table/newgrf_debug_data.h table/object_land.h +table/opengl_shader.h table/palette_convert.h table/palettes.h table/pricebase.h
new file mode 100644 --- /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);", + "}", +};
--- a/src/video/opengl.cpp +++ b/src/video/opengl.cpp @@ -34,6 +34,8 @@ #include "../gfx_func.h" #include "../debug.h" +#include "../table/opengl_shader.h" + static PFNGLGENBUFFERSPROC _glGenBuffers; static PFNGLDELETEBUFFERSPROC _glDeleteBuffers; @@ -46,6 +48,22 @@ 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; @@ -192,6 +210,49 @@ return _glGenVertexArrays != NULL && _glDeleteVertexArrays != NULL && _glBindVertexArray != NULL; } +/** 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 != NULL && _glDeleteProgram != NULL && _glLinkProgram != NULL && _glGetProgramiv != NULL && _glGetProgramInfoLog != NULL && + _glCreateShader != NULL && _glDeleteShader != NULL && _glShaderSource != NULL && _glCompileShader != NULL && _glAttachShader != NULL && + _glGetShaderiv != NULL && _glGetShaderInfoLog != NULL && _glGetUniformLocation != NULL && _glUniform1i != NULL; +} + /** * Construct OpenGL back-end class. @@ -205,6 +266,7 @@ */ OpenGLBackend::~OpenGLBackend() { + _glDeleteProgram(this->vid_program); _glDeleteVertexArrays(1, &this->vao_quad); _glDeleteBuffers(1, &this->vbo_quad); _glDeleteBuffers(1, &this->vid_pbo); @@ -237,6 +299,13 @@ /* 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")) 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); @@ -249,6 +318,11 @@ 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); @@ -273,6 +347,7 @@ _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); @@ -282,12 +357,88 @@ glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glDisable(GL_DEPTH_TEST); - glEnable(GL_TEXTURE_2D); return NULL; } /** + * 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; + _glGetShaderiv(shader, GL_COMPILE_STATUS, &result); + + /* Output log if there is one. */ + GLint log_len; + _glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + _glGetShaderInfoLog(shader, log_len, NULL, 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; + _glGetProgramiv(program, GL_LINK_STATUS, &result); + + /* Output log if there is one. */ + GLint log_len; + _glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + _glGetProgramInfoLog(program, log_len, NULL, 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, NULL); + _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, NULL); + _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. @@ -325,6 +476,7 @@ /* 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);
--- a/src/video/opengl.h +++ b/src/video/opengl.h @@ -24,9 +24,12 @@ 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. + bool InitShaders(); + public: OpenGLBackend(); ~OpenGLBackend();