diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 66b68c2c3d..3e198b1a04 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -109,7 +109,8 @@ void FGLRenderer::Initialize() mVBO = new FFlatVertexBuffer; mSkyVBO = new FSkyVertexBuffer; - mLights = new FLightBuffer(); + if (gl.lightmethod != LM_SOFTWARE) mLights = new FLightBuffer(); + else mLights = NULL; gl_RenderState.SetVertexBuffer(mVBO); mFBID = 0; SetupLevel(); diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index d7f46e07ef..9b9d2d7e52 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -309,7 +309,7 @@ void FRenderState::ApplyMatrices() void FRenderState::ApplyLightIndex(int index) { - if (GLRenderer->mLights->GetBufferType() == GL_UNIFORM_BUFFER && index > -1) + if (index > -1 && GLRenderer->mLights->GetBufferType() == GL_UNIFORM_BUFFER) { index = GLRenderer->mLights->BindUBO(index); } diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index 0ec2b51496..c90e9541b9 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -193,7 +193,7 @@ void GLFlat::DrawSubsector(subsector_t * sub) //========================================================================== // -// +// this is only used by LM_DEFERRED // //========================================================================== @@ -399,7 +399,7 @@ void GLFlat::Draw(int pass, bool trans) // trans only has meaning for GLPASS_LIG switch (pass) { case GLPASS_PLAIN: // Single-pass rendering - case GLPASS_ALL: + case GLPASS_ALL: // Same, but also creates the dynlight data. gl_SetColor(lightlevel, rel, Colormap,1.0f); gl_SetFog(lightlevel, rel, &Colormap, false); if (sector->special != GLSector_Skybox) @@ -473,6 +473,7 @@ inline void GLFlat::PutFlat(bool fog) bool masked = gltexture->isMasked() && ((renderflags&SSRF_RENDER3DPLANES) || stack); list = masked ? GLDL_MASKEDFLATS : GLDL_PLAINFLATS; } + dynlightindex = -1; // make sure this is always initialized to something proper. gl_drawinfo->drawlists[list].AddFlat (this); } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 9c2f6505cb..7062f0cb60 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -351,7 +351,8 @@ void FGLRenderer::RenderScene(int recursion) // if we don't have a persistently mapped buffer, we have to process all the dynamic lights up front, // so that we don't have to do repeated map/unmap calls on the buffer. - if (mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights && !(gl.flags & RFL_BUFFER_STORAGE)) + bool haslights = mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights; + if (gl.lightmethod == LM_DEFERRED && haslights) { GLRenderer->mLights->Begin(); gl_drawinfo->drawlists[GLDL_PLAINWALLS].DrawWalls(GLPASS_LIGHTSONLY); @@ -371,12 +372,20 @@ void FGLRenderer::RenderScene(int recursion) int pass; - if (mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights && (gl.flags & RFL_BUFFER_STORAGE)) + if (!haslights || gl.lightmethod == LM_DEFERRED) + { + pass = GLPASS_PLAIN; + } + else if (gl.lightmethod == LM_DIRECT) { pass = GLPASS_ALL; } else { + // Todo: Draw lights with multipass rendering. + // RenderMultpassStuff(); + + // The remaining stuff which is unaffected by dynamic lights is just processed as normal. pass = GLPASS_PLAIN; } @@ -418,6 +427,10 @@ void FGLRenderer::RenderScene(int recursion) // this is the only geometry type on which decals can possibly appear gl_drawinfo->drawlists[GLDL_PLAINWALLS].DrawDecals(); + if (gl.lightmethod == LM_SOFTWARE) + { + // also process the render lists with walls and dynamic lights + } gl_RenderState.SetTextureMode(TM_MODULATE); @@ -870,7 +883,7 @@ void FGLRenderer::RenderView (player_t* player) P_FindParticleSubsectors (); - GLRenderer->mLights->Clear(); + if (gl.lightmethod != LM_SOFTWARE) GLRenderer->mLights->Clear(); // NoInterpolateView should have no bearing on camera textures, but needs to be preserved for the main view below. bool saved_niv = NoInterpolateView; @@ -925,7 +938,7 @@ void FGLRenderer::WriteSavePic (player_t *player, FILE *file, int width, int hei SetFixedColormap(player); gl_RenderState.SetVertexBuffer(mVBO); GLRenderer->mVBO->Reset(); - GLRenderer->mLights->Clear(); + if (gl.lightmethod != LM_SOFTWARE) GLRenderer->mLights->Clear(); // Check if there's some lights. If not some code can be skipped. TThinkerIterator it(STAT_DLIGHT); diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp index 49b2411a9c..caf03322dc 100644 --- a/src/gl/scene/gl_walls.cpp +++ b/src/gl/scene/gl_walls.cpp @@ -1422,7 +1422,7 @@ void GLWall::Process(seg_t *seg, sector_t * frontsector, sector_t * backsector) glseg.y2 = v2->fY(); Colormap = frontsector->ColorMap; flags = 0; - dynlightindex = UINT_MAX; + dynlightindex = -1; lightlist = NULL; int rel = 0; @@ -1681,7 +1681,7 @@ void GLWall::ProcessLowerMiniseg(seg_t *seg, sector_t * frontsector, sector_t * bottomflat = frontsector->GetTexture(sector_t::floor); topplane = frontsector->ceilingplane; bottomplane = frontsector->floorplane; - dynlightindex = UINT_MAX; + dynlightindex = -1; zfloor[0] = zfloor[1] = ffh; diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index aa8dfac2b9..493d81818c 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -87,25 +87,37 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * // // The following code uses GetChars on the strings to get rid of terminating 0 characters. Do not remove or the code may break! // - unsigned int lightbuffertype = GLRenderer->mLights->GetBufferType(); - unsigned int lightbuffersize = GLRenderer->mLights->GetBlockSize(); - FString vp_comb; - if (lightbuffertype == GL_UNIFORM_BUFFER) + if (gl.lightmethod == LM_SOFTWARE) { - if (gl.glslversion < 1.4f || gl.version < 3.1f) + if (gl.compatibility >= CMPT_GL3) { - vp_comb.Format("#version 130\n#extension GL_ARB_uniform_buffer_object : require\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize); - } - else - { - vp_comb.Format("#version 140\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize); + vp_comb = "#version 130\n"; } } else { - vp_comb = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n"; + assert(GLRenderer->mLights != NULL); + // On the shader side there is no difference between LM_DEFERRED and LM_DIRECT, it only matters which buffer type is used by the light buffer. + unsigned int lightbuffertype = GLRenderer->mLights->GetBufferType(); + unsigned int lightbuffersize = GLRenderer->mLights->GetBlockSize(); + if (lightbuffertype == GL_UNIFORM_BUFFER) + { + // This differentiation is for some Intel drivers which fail on #extension, so use of #version 140 is necessary + if (gl.glslversion < 1.4f || gl.version < 3.1f) + { + vp_comb.Format("#version 130\n#extension GL_ARB_uniform_buffer_object : require\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize); + } + else + { + vp_comb.Format("#version 140\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize); + } + } + else + { + vp_comb = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n"; + } } vp_comb << defines << i_data.GetString().GetChars(); diff --git a/src/gl/system/gl_interface.cpp b/src/gl/system/gl_interface.cpp index 7b46d1a5e2..d057ad1406 100644 --- a/src/gl/system/gl_interface.cpp +++ b/src/gl/system/gl_interface.cpp @@ -107,35 +107,77 @@ static void InitContext() // //========================================================================== +#define FUDGE_FUNC(name, ext) if (_ptrc_##name == NULL) _ptrc_##name = _ptrc_##name##ext; + + void gl_LoadExtensions() { InitContext(); CollectExtensions(); const char *version = Args->CheckValue("-glversion"); - if (version == NULL) version = (const char*)glGetString(GL_VERSION); - else Printf("Emulating OpenGL v %s\n", version); + const char *glversion = (const char*)glGetString(GL_VERSION); + + if (version == NULL) + { + version = glversion; + } + else + { + double v1 = strtod(version, NULL); + double v2 = strtod(glversion, NULL); + if (v2 < v1) version = glversion; + else Printf("Emulating OpenGL v %s\n", version); + } + + gl.version = strtod(version, NULL) + 0.01f; // Don't even start if it's lower than 3.0 - if (strcmp(version, "3.0") < 0) + if ((gl.version < 2.0 || !CheckExtension("GL_EXT_framebuffer_object")) && gl.version < 3.0) { - I_FatalError("Unsupported OpenGL version.\nAt least OpenGL 3.0 is required to run " GAMENAME ".\n"); + I_FatalError("Unsupported OpenGL version.\nAt least OpenGL 2.0 with framebuffer support is required to run " GAMENAME ".\n"); } // add 0.01 to account for roundoff errors making the number a tad smaller than the actual version - gl.version = strtod(version, NULL) + 0.01f; gl.glslversion = strtod((char*)glGetString(GL_SHADING_LANGUAGE_VERSION), NULL) + 0.01f; gl.vendorstring = (char*)glGetString(GL_VENDOR); + gl.lightmethod = LM_SOFTWARE; if (gl.version >= 3.3f || CheckExtension("GL_ARB_sampler_objects")) { gl.flags |= RFL_SAMPLER_OBJECTS; } + + // Buffer lighting is only feasible with GLSL 1.3 and higher, even if 1.2 supports the extension. + if (gl.version > 3.0f && (gl.version >= 3.3f || CheckExtension("GL_ARB_uniform_buffer_object"))) + { + gl.flags |= RFL_SAMPLER_OBJECTS; + gl.lightmethod = LM_DEFERRED; + } - if (CheckExtension("GL_ARB_texture_compression")) gl.flags|=RFL_TEXTURE_COMPRESSION; - if (CheckExtension("GL_EXT_texture_compression_s3tc")) gl.flags|=RFL_TEXTURE_COMPRESSION_S3TC; - if (!Args->CheckParm("-gl3")) + if (CheckExtension("GL_ARB_texture_compression")) gl.flags |= RFL_TEXTURE_COMPRESSION; + if (CheckExtension("GL_EXT_texture_compression_s3tc")) gl.flags |= RFL_TEXTURE_COMPRESSION_S3TC; + + if (Args->CheckParm("-noshader")) + { + gl.compatibility = CMPT_GL2; // force the low end path + } + if (gl.version < 3.0f) + { + if (CheckExtension("GL_NV_GPU_shader4") || CheckExtension("GL_EXT_GPU_shader4")) gl.compatibility = CMPT_GL2_SHADER; // for pre-3.0 drivers that support capable hardware. Needed for Apple. + else gl.compatibility = CMPT_GL2; + } + else if (gl.version < 4.f) + { + if (strstr(gl.vendorstring, "ATI Tech")) + { + gl.compatibility = CMPT_GL2_SHADER; // most of these drivers are irreperably broken with GLSL 1.3 and higher. + gl.lightmethod = LM_SOFTWARE; // do not use uniform buffers with the fallback shader, it may cause problems. + } + else gl.compatibility = CMPT_GL3; + } + else { // don't use GL 4.x features when running in GL 3 emulation mode. if (CheckExtension("GL_ARB_buffer_storage")) @@ -151,9 +193,22 @@ void gl_LoadExtensions() } } gl.flags |= RFL_BUFFER_STORAGE; + gl.compatibility = CMPT_GL4; + gl.lightmethod = LM_DIRECT; + } + else + { + gl.compatibility = CMPT_GL3; } } - + + const char *lm = Args->CheckValue("-lightmethod"); + if (lm != NULL) + { + if (!stricmp(lm, "deferred") && gl.lightmethod == LM_DIRECT) gl.lightmethod = LM_DEFERRED; + if (!stricmp(lm, "textured")) gl.lightmethod = LM_SOFTWARE; + } + int v; glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &v); gl.maxuniforms = v; @@ -161,9 +216,26 @@ void gl_LoadExtensions() gl.maxuniformblock = v; glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &v); gl.uniformblockalignment = v; - - glGetIntegerv(GL_MAX_TEXTURE_SIZE,&gl.max_texturesize); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl.max_texturesize); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // fudge a bit with the framebuffer stuff to avoid redundancies in the main code. Some of the older cards do not have the ARB stuff but the calls are nearly identical. + FUDGE_FUNC(glGenerateMipmap, EXT); + FUDGE_FUNC(glGenFramebuffers, EXT); + FUDGE_FUNC(glBindFramebuffer, EXT); + FUDGE_FUNC(glDeleteFramebuffers, EXT); + FUDGE_FUNC(glFramebufferTexture2D, EXT); + FUDGE_FUNC(glGenerateMipmap, EXT); + FUDGE_FUNC(glGenFramebuffers, EXT); + FUDGE_FUNC(glBindFramebuffer, EXT); + FUDGE_FUNC(glDeleteFramebuffers, EXT); + FUDGE_FUNC(glFramebufferTexture2D, EXT); + FUDGE_FUNC(glFramebufferRenderbuffer, EXT); + FUDGE_FUNC(glGenRenderbuffers, EXT); + FUDGE_FUNC(glDeleteRenderbuffers, EXT); + FUDGE_FUNC(glRenderbufferStorage, EXT); + FUDGE_FUNC(glBindRenderbuffer, EXT); } //========================================================================== diff --git a/src/gl/system/gl_interface.h b/src/gl/system/gl_interface.h index 6988f873e1..5248c2f8b1 100644 --- a/src/gl/system/gl_interface.h +++ b/src/gl/system/gl_interface.h @@ -3,6 +3,14 @@ #include "basictypes.h" +enum GLCompat +{ + CMPT_GL2, + CMPT_GL2_SHADER, + CMPT_GL3, + CMPT_GL4 +}; + enum RenderFlags { // [BB] Added texture compression flags. @@ -11,7 +19,7 @@ enum RenderFlags RFL_SHADER_STORAGE_BUFFER = 4, RFL_BUFFER_STORAGE = 8, - RFL_SAMPLER_OBJECTS = 16 + RFL_SAMPLER_OBJECTS = 16, }; enum TexMode @@ -22,6 +30,15 @@ enum TexMode TM_INVERSE, // (1-r, 1-g, 1-b, a) TM_REDTOALPHA, // (1, 1, 1, r) TM_CLAMPY, // (r, g, b, (t >= 0.0 && t <= 1.0)? a:0) + + TM_INVERTOPAQUE, // used by GL 2.x fallback code. +}; + +enum ELightMethod +{ + LM_SOFTWARE = 0, // multi-pass texturing + LM_DEFERRED = 1, // calculate lights up front in a separate pass + LM_DIRECT = 2, // calculate lights on the fly along with the render data }; struct RenderContext @@ -30,6 +47,8 @@ struct RenderContext unsigned int maxuniforms; unsigned int maxuniformblock; unsigned int uniformblockalignment; + int lightmethod; + int compatibility; float version; float glslversion; int max_texturesize;