From aeedfb4aa3b3fb1fbfa79f1896e9d245b72141ba Mon Sep 17 00:00:00 2001 From: Giulio Camuffo Date: Wed, 7 Mar 2012 16:12:14 +0100 Subject: [PATCH] GRIM: Use an ARB shader to dim a screen region if possible. This is much faster than using glReadPixels/glDrawPixels. --- engines/grim/gfx_opengl.cpp | 99 ++++++++++++++++++++++++++++++++++++- engines/grim/gfx_opengl.h | 2 + 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/engines/grim/gfx_opengl.cpp b/engines/grim/gfx_opengl.cpp index f9fbd25e1bb..f82ca1b1d50 100644 --- a/engines/grim/gfx_opengl.cpp +++ b/engines/grim/gfx_opengl.cpp @@ -57,6 +57,7 @@ PFNGLGENPROGRAMSARBPROC glGenProgramsARB; PFNGLBINDPROGRAMARBPROC glBindProgramARB; PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; +PFNGLPROGRAMLOCALPARAMETER4FARBPROC glProgramLocalParameter4fARB; #endif @@ -74,6 +75,23 @@ static char fragSrc[] = MOV result.depth, d.r;\n\ END\n"; +static char dimFragSrc[] = + "!!ARBfp1.0\n\ + PARAM level = program.local[0];\n\ + TEMP color;\n\ + TEMP d;\n\ + TEX d, fragment.texcoord[0], texture[0], 2D;\n\ + TEMP sum;\n\ + MOV sum, d.r;\n\ + ADD sum, sum, d.g;\n\ + ADD sum, sum, d.b;\n\ + MUL sum, sum, 0.33;\n\ + MUL sum, sum, level.x;\n\ + MOV result.color.r, sum;\n\ + MOV result.color.g, sum;\n\ + MOV result.color.b, sum;\n\ + END\n"; + GfxOpenGL::GfxOpenGL() { g_driver = this; _storedDisplay = NULL; @@ -89,6 +107,9 @@ GfxOpenGL::~GfxOpenGL() { #ifdef GL_ARB_fragment_program if (_useDepthShader) glDeleteProgramsARB(1, &_fragmentProgram); + + if (_useDimShader) + glDeleteProgramsARB(1, &_dimFragProgram); #endif } @@ -102,6 +123,7 @@ byte *GfxOpenGL::setupScreen(int screenW, int screenH, bool fullscreen) { _isFullscreen = g_system->getFeatureState(OSystem::kFeatureFullscreenMode); _useDepthShader = false; + _useDimShader = false; g_system->showMouse(!fullscreen); @@ -148,10 +170,13 @@ void GfxOpenGL::initExtensions() glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC)u.func_ptr; u.obj_ptr = SDL_GL_GetProcAddress("glDeleteProgramsARB"); glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC)u.func_ptr; + u.obj_ptr = SDL_GL_GetProcAddress("glProgramLocalParameter4fARB"); + glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC)u.func_ptr; const char* extensions = (const char*)glGetString(GL_EXTENSIONS); if (strstr(extensions, "ARB_fragment_program")) { _useDepthShader = true; + _useDimShader = true; } if (_useDepthShader) { @@ -162,9 +187,20 @@ void GfxOpenGL::initExtensions() glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(fragSrc), fragSrc); glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); if (errorPos != -1) { - warning("Error compiling fragment program:\n%s", glGetString(GL_PROGRAM_ERROR_STRING_ARB)); + warning("Error compiling depth fragment program:\n%s", glGetString(GL_PROGRAM_ERROR_STRING_ARB)); _useDepthShader = false; } + + + glGenProgramsARB(1, &_dimFragProgram); + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, _dimFragProgram); + + glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(dimFragSrc), dimFragSrc); + glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos); + if (errorPos != -1) { + warning("Error compiling dim fragment program:\n%s", glGetString(GL_PROGRAM_ERROR_STRING_ARB)); + _useDimShader = false; + } } #endif } @@ -780,6 +816,7 @@ void GfxOpenGL::drawBitmap(const Bitmap *bitmap, int dx, int dy) { glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glDepthMask(GL_TRUE); #ifdef GL_ARB_fragment_program + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, _fragmentProgram); glEnable(GL_FRAGMENT_PROGRAM_ARB); #endif } @@ -1303,8 +1340,66 @@ void GfxOpenGL::dimRegion(int x, int yReal, int w, int h, float level) { yReal = (int)(yReal *_scaleH); w = (int)(w * _scaleW); h = (int)(h * _scaleH); + int y = _screenHeight - yReal - h; + +#ifdef GL_ARB_fragment_program + if (_useDimShader) { + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glViewport(0, 0, _screenWidth, _screenHeight); + + // copy the data over to the texture + glBindTexture(GL_TEXTURE_2D, texture); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, x, y, w, h, 0); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, _screenWidth, 0, _screenHeight, 0, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glDisable(GL_ALPHA_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_SCISSOR_TEST); + + glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, _dimFragProgram); + glEnable(GL_FRAGMENT_PROGRAM_ARB); + glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, level, 0, 0, 0); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + glBegin(GL_QUADS); + glTexCoord2f(0, 0); + glVertex2f(x, y); + glTexCoord2f(1.0f, 0.0f); + glVertex2f(x + w, y); + glTexCoord2f(1.0f, 1.0f); + glVertex2f(x + w, y + h); + glTexCoord2f(0.0f, 1.0f); + glVertex2f(x, y + h); + glEnd(); + + glDisable(GL_FRAGMENT_PROGRAM_ARB); + + glDeleteTextures(1, &texture); + + return; + } +#endif + uint32 *data = new uint32[w * h]; - int y = _screenHeight - yReal; + y = _screenHeight - yReal; // collect the requested area and generate the dimmed version glReadPixels(x, y - h, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data); diff --git a/engines/grim/gfx_opengl.h b/engines/grim/gfx_opengl.h index 8f0eac122e4..39a5bef8664 100644 --- a/engines/grim/gfx_opengl.h +++ b/engines/grim/gfx_opengl.h @@ -131,6 +131,8 @@ private: byte *_storedDisplay; bool _useDepthShader; GLuint _fragmentProgram; + bool _useDimShader; + GLuint _dimFragProgram; int _maxLights; };