From 3299a29c446ea13ee110f75f527acc179c78ed72 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 28 Aug 2016 16:14:24 +0200 Subject: [PATCH 01/35] - added CheckClass ACS function. --- src/p_acs.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 5f08f0152..a2b5275cb 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4449,6 +4449,8 @@ enum EACSFunctions -106 : KickFromGame(2), */ + ACSF_CheckClass = 200, + // ZDaemon ACSF_GetTeamScore = 19620, // (int team) ACSF_SetTeamScore, // (int team, int value) @@ -6028,6 +6030,12 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return false; } + case ACSF_CheckClass: + { + const char *clsname = FBehavior::StaticLookupString(args[0]); + return !!PClass::FindActor(clsname); + } + default: break; } From 49930185206141a5d0219ebbbf405cae88c82f0e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 31 Aug 2016 09:18:59 +0200 Subject: [PATCH 02/35] - fixed: Actor velocity requires an upper limit to prevent uncontrolled accumulation, as can happen when multiple exploding and pushable objects overlap. The value 5000 was chosen because it is high enough to not occur under regular circumstances and small enough to prevent severe slowdowns. In the old fixed point code the lack of such a check just caused random overflows. --- src/p_mobj.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 8971efe25..f5791d079 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1806,6 +1806,11 @@ double P_XYMovement (AActor *mo, DVector2 scroll) mo->Vel.X *= fac; mo->Vel.Y *= fac; } + const double VELOCITY_THRESHOLD = 5000; // don't let it move faster than this. Fixed point overflowed at 32768 but that's too much to make this safe. + if (mo->Vel.LengthSquared() >= VELOCITY_THRESHOLD*VELOCITY_THRESHOLD) + { + mo->Vel.MakeResize(VELOCITY_THRESHOLD); + } move = mo->Vel; // [RH] Carrying sectors didn't work with low speeds in BOOM. This is // because BOOM relied on the speed being fast enough to accumulate From 4a0e0828366dda0fb6de69b734c4e190ba9c6026 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 31 Aug 2016 23:26:49 +0200 Subject: [PATCH 03/35] - fixed: The wall splitter in the translucent sorting code needs to set fracleft and fracright so that vertex generation is done correctly for the split segments. --- src/gl/scene/gl_drawinfo.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index 842423094..d5ed62755 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -476,6 +476,7 @@ void GLDrawList::SortWallIntoWall(SortNode * head,SortNode * sort) ws1->glseg.x1=ws->glseg.x2=ix; ws1->glseg.y1=ws->glseg.y2=iy; + ws1->glseg.fracleft = ws->glseg.fracright = ws->glseg.fracleft + r*(ws->glseg.fracright - ws->glseg.fracleft); ws1->ztop[0]=ws->ztop[1]=izt; ws1->zbottom[0]=ws->zbottom[1]=izb; ws1->tcs[GLWall::LOLFT].u = ws1->tcs[GLWall::UPLFT].u = ws->tcs[GLWall::LORGT].u = ws->tcs[GLWall::UPRGT].u = iu; From 3389a5a74edbc240a6618336b1cf917cea4144b1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 1 Sep 2016 11:52:52 +0200 Subject: [PATCH 04/35] - removed most of the specific options for legacy hardware and consolidated them in one variable (does not work yet.) --- src/gl/compatibility/gl_20.cpp | 35 +---- src/gl/data/gl_vertexbuffer.cpp | 27 ++-- src/gl/models/gl_models.cpp | 10 +- src/gl/renderer/gl_postprocess.cpp | 6 +- src/gl/renderer/gl_renderbuffers.cpp | 17 +-- src/gl/renderer/gl_renderer.cpp | 6 +- src/gl/renderer/gl_renderstate.cpp | 4 +- src/gl/renderer/gl_renderstate.h | 2 +- src/gl/scene/gl_drawinfo.cpp | 17 ++- src/gl/scene/gl_drawinfo.h | 2 - src/gl/scene/gl_flats.cpp | 4 +- src/gl/scene/gl_scene.cpp | 12 +- src/gl/scene/gl_skydome.cpp | 4 +- src/gl/scene/gl_sprite.cpp | 2 +- src/gl/scene/gl_walls.cpp | 2 +- src/gl/scene/gl_walls_draw.cpp | 9 +- src/gl/shaders/gl_blurshader.cpp | 22 --- src/gl/shaders/gl_shader.cpp | 50 +++---- src/gl/shaders/gl_shaderprogram.cpp | 50 +------ src/gl/shaders/gl_shaderprogram.h | 5 - src/gl/system/gl_framebuffer.cpp | 2 +- src/gl/system/gl_interface.cpp | 194 ++++++++++++-------------- src/gl/system/gl_interface.h | 16 +-- src/gl/textures/gl_hqresize.cpp | 2 +- src/gl/textures/gl_material.cpp | 8 +- wadsrc/static/shaders/glsl/main.fp | 27 ---- wadsrc/static/shaders/glsl/tonemap.fp | 7 - 27 files changed, 182 insertions(+), 360 deletions(-) diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp index 8e6682f17..97abc0d82 100644 --- a/src/gl/compatibility/gl_20.cpp +++ b/src/gl/compatibility/gl_20.cpp @@ -66,7 +66,7 @@ void gl_PatchMenu() { - if (gl.glslversion == 0) + if (gl.legacyMode) { // Radial fog and Doom lighting are not available without full shader support. @@ -99,6 +99,8 @@ void gl_PatchMenu() // disable features that don't work without shaders. if (gl_lightmode == 2 || gl_lightmode == 8) gl_lightmode = 3; if (gl_fogmode == 2) gl_fogmode = 1; + + // todo: remove more unsupported stuff like postprocessing options. } } @@ -378,7 +380,7 @@ void FRenderState::DrawColormapOverlay() //========================================================================== bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, Vector & up, Vector & right, - float & scale, int desaturation, bool checkside, bool additive) + float & scale, bool checkside, bool additive) { Vector fn, pos; @@ -434,14 +436,6 @@ bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, { gl_RenderState.BlendEquation(GL_FUNC_ADD); } - if (desaturation > 0 && gl.glslversion > 0) // no-shader excluded because no desaturated textures. - { - float gray = (r * 77 + g * 143 + b * 37) / 257; - - r = (r*(32 - desaturation) + gray*desaturation) / 32; - g = (g*(32 - desaturation) + gray*desaturation) / 32; - b = (b*(32 - desaturation) + gray*desaturation) / 32; - } gl_RenderState.SetColor(r, g, b); return true; } @@ -491,11 +485,6 @@ bool GLWall::PutWallCompat(int passflag) bool masked = passflag == 2 && gltexture->isMasked(); int list = list_indices[masked][foggy]; - if (list == GLLDL_WALLS_PLAIN) - { - if (gltexture->tex->gl_info.Brightmap && gl.glslversion >= 0.f) list = GLLDL_WALLS_BRIGHT; - //if (flags & GLWF_GLOW) list = GLLDL_WALLS_BRIGHT; - } gl_drawinfo->dldrawlists[list].AddWall(this); return true; @@ -520,10 +509,6 @@ bool GLFlat::PutFlatCompat(bool fog) int list = list_indices[masked][foggy]; - if (list == GLLDL_FLATS_PLAIN) - { - if (gltexture->tex->gl_info.Brightmap && gl.glslversion >= 0.f) list = GLLDL_FLATS_BRIGHT; - } gl_drawinfo->dldrawlists[list].AddFlat(this); return true; } @@ -612,7 +597,7 @@ void GLFlat::DrawSubsectorLights(subsector_t * sub, int pass) } p.Set(plane.plane); - if (!gl_SetupLight(sub->sector->PortalGroup, p, light, nearPt, up, right, scale, CM_DEFAULT, false, pass != GLPASS_LIGHTTEX)) + if (!gl_SetupLight(sub->sector->PortalGroup, p, light, nearPt, up, right, scale, false, pass != GLPASS_LIGHTTEX)) { node = node->nextLight; continue; @@ -692,7 +677,7 @@ bool GLWall::PrepareLight(ADynamicLight * light, int pass) return false; } - if (!gl_SetupLight(seg->frontsector->PortalGroup, p, light, nearPt, up, right, scale, CM_DEFAULT, true, pass != GLPASS_LIGHTTEX)) + if (!gl_SetupLight(seg->frontsector->PortalGroup, p, light, nearPt, up, right, scale, true, pass != GLPASS_LIGHTTEX)) { return false; } @@ -798,9 +783,7 @@ void FGLRenderer::RenderMultipassStuff() gl_RenderState.SetTextureMode(TM_MASK); gl_RenderState.EnableBrightmap(true); gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold); - gl_drawinfo->dldrawlists[GLLDL_WALLS_BRIGHT].DrawWalls(GLPASS_PLAIN); gl_drawinfo->dldrawlists[GLLDL_WALLS_MASKED].DrawWalls(GLPASS_PLAIN); - gl_drawinfo->dldrawlists[GLLDL_FLATS_BRIGHT].DrawFlats(GLPASS_PLAIN); gl_drawinfo->dldrawlists[GLLDL_FLATS_MASKED].DrawFlats(GLPASS_PLAIN); // Part 3: The base of fogged surfaces, including the texture @@ -823,10 +806,8 @@ void FGLRenderer::RenderMultipassStuff() glDepthFunc(GL_EQUAL); if (glset.lightmode == 8) gl_RenderState.SetSoftLightLevel(255); gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawWalls(GLPASS_LIGHTTEX); - gl_drawinfo->dldrawlists[GLLDL_WALLS_BRIGHT].DrawWalls(GLPASS_LIGHTTEX); gl_drawinfo->dldrawlists[GLLDL_WALLS_MASKED].DrawWalls(GLPASS_LIGHTTEX); gl_drawinfo->dldrawlists[GLLDL_FLATS_PLAIN].DrawFlats(GLPASS_LIGHTTEX); - gl_drawinfo->dldrawlists[GLLDL_FLATS_BRIGHT].DrawFlats(GLPASS_LIGHTTEX); gl_drawinfo->dldrawlists[GLLDL_FLATS_MASKED].DrawFlats(GLPASS_LIGHTTEX); gl_RenderState.BlendEquation(GL_FUNC_ADD); } @@ -841,8 +822,6 @@ void FGLRenderer::RenderMultipassStuff() glDepthFunc(GL_LEQUAL); gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawWalls(GLPASS_TEXONLY); gl_drawinfo->dldrawlists[GLLDL_FLATS_PLAIN].DrawFlats(GLPASS_TEXONLY); - gl_drawinfo->dldrawlists[GLLDL_WALLS_BRIGHT].DrawWalls(GLPASS_TEXONLY); - gl_drawinfo->dldrawlists[GLLDL_FLATS_BRIGHT].DrawFlats(GLPASS_TEXONLY); gl_RenderState.AlphaFunc(GL_GREATER, gl_mask_threshold); gl_drawinfo->dldrawlists[GLLDL_WALLS_MASKED].DrawWalls(GLPASS_TEXONLY); gl_drawinfo->dldrawlists[GLLDL_FLATS_MASKED].DrawFlats(GLPASS_TEXONLY); @@ -854,10 +833,8 @@ void FGLRenderer::RenderMultipassStuff() if (gl_SetupLightTexture()) { gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawWalls(GLPASS_LIGHTTEX_ADDITIVE); - gl_drawinfo->dldrawlists[GLLDL_WALLS_BRIGHT].DrawWalls(GLPASS_LIGHTTEX_ADDITIVE); gl_drawinfo->dldrawlists[GLLDL_WALLS_MASKED].DrawWalls(GLPASS_LIGHTTEX_ADDITIVE); gl_drawinfo->dldrawlists[GLLDL_FLATS_PLAIN].DrawFlats(GLPASS_LIGHTTEX_ADDITIVE); - gl_drawinfo->dldrawlists[GLLDL_FLATS_BRIGHT].DrawFlats(GLPASS_LIGHTTEX_ADDITIVE); gl_drawinfo->dldrawlists[GLLDL_FLATS_MASKED].DrawFlats(GLPASS_LIGHTTEX_ADDITIVE); gl_drawinfo->dldrawlists[GLLDL_WALLS_FOG].DrawWalls(GLPASS_LIGHTTEX_FOGGY); gl_drawinfo->dldrawlists[GLLDL_WALLS_FOGMASKED].DrawWalls(GLPASS_LIGHTTEX_FOGGY); diff --git a/src/gl/data/gl_vertexbuffer.cpp b/src/gl/data/gl_vertexbuffer.cpp index 5833d3109..0309b2bcd 100644 --- a/src/gl/data/gl_vertexbuffer.cpp +++ b/src/gl/data/gl_vertexbuffer.cpp @@ -75,7 +75,7 @@ FVertexBuffer::~FVertexBuffer() void FSimpleVertexBuffer::BindVBO() { glBindBuffer(GL_ARRAY_BUFFER, vbo_id); - if (gl.glslversion > 0) + if (!gl.legacyMode) { glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FSimpleVertex), &VSiO->x); glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FSimpleVertex), &VSiO->u); @@ -100,7 +100,7 @@ void FSimpleVertexBuffer::EnableColorArray(bool on) { if (on) { - if (gl.glslversion > 0) + if (!gl.legacyMode) { glEnableVertexAttribArray(VATTR_COLOR); } @@ -111,7 +111,7 @@ void FSimpleVertexBuffer::EnableColorArray(bool on) } else { - if (gl.glslversion > 0) + if (!gl.legacyMode) { glDisableVertexAttribArray(VATTR_COLOR); } @@ -138,7 +138,7 @@ void FSimpleVertexBuffer::set(FSimpleVertex *verts, int count) //========================================================================== FFlatVertexBuffer::FFlatVertexBuffer(int width, int height) -: FVertexBuffer(gl.buffermethod != BM_CLIENTARRAY) +: FVertexBuffer(!gl.legacyMode) { switch (gl.buffermethod) { @@ -162,7 +162,7 @@ FFlatVertexBuffer::FFlatVertexBuffer(int width, int height) break; } - case BM_CLIENTARRAY: + default: { map = new FFlatVertex[BUFFER_SIZE]; DPrintf(DMSG_NOTIFY, "Using client array buffer\n"); @@ -219,7 +219,7 @@ FFlatVertexBuffer::~FFlatVertexBuffer() glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, 0); } - if (gl.buffermethod == BM_CLIENTARRAY) + if (gl.legacyMode) { delete[] map; } @@ -230,19 +230,10 @@ FFlatVertexBuffer::~FFlatVertexBuffer() void FFlatVertexBuffer::BindVBO() { glBindBuffer(GL_ARRAY_BUFFER, vbo_id); - if (gl.glslversion > 0) + if (!gl.legacyMode) { - if (gl.buffermethod != BM_CLIENTARRAY) - { - glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FFlatVertex), &VTO->x); - glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FFlatVertex), &VTO->u); - } - else - { - // If we cannot use a hardware buffer, use an old-style client array. - glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FFlatVertex), &map->x); - glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FFlatVertex), &map->u); - } + glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FFlatVertex), &VTO->x); + glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FFlatVertex), &VTO->u); glEnableVertexAttribArray(VATTR_VERTEX); glEnableVertexAttribArray(VATTR_TEXCOORD); glDisableVertexAttribArray(VATTR_COLOR); diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index e686f2cea..22b61ace4 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -105,7 +105,7 @@ void gl_FlushModels() //=========================================================================== FModelVertexBuffer::FModelVertexBuffer(bool needindex, bool singleframe) - : FVertexBuffer(singleframe || gl.glslversion > 0) + : FVertexBuffer(singleframe || !gl.legacyMode) { vbo_ptr = nullptr; ibo_id = 0; @@ -125,7 +125,7 @@ void FModelVertexBuffer::BindVBO() { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id); glBindBuffer(GL_ARRAY_BUFFER, vbo_id); - if (gl.glslversion > 0) + if (!gl.legacyMode) { glEnableVertexAttribArray(VATTR_VERTEX); glEnableVertexAttribArray(VATTR_TEXCOORD); @@ -170,7 +170,7 @@ FModelVertex *FModelVertexBuffer::LockVertexBuffer(unsigned int size) { glBindBuffer(GL_ARRAY_BUFFER, vbo_id); glBufferData(GL_ARRAY_BUFFER, size * sizeof(FModelVertex), nullptr, GL_STATIC_DRAW); - if (gl.version >= 3.0) + if (!gl.legacyMode) return (FModelVertex*)glMapBufferRange(GL_ARRAY_BUFFER, 0, size * sizeof(FModelVertex), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); else return (FModelVertex*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); @@ -211,7 +211,7 @@ unsigned int *FModelVertexBuffer::LockIndexBuffer(unsigned int size) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id); glBufferData(GL_ELEMENT_ARRAY_BUFFER, size * sizeof(unsigned int), NULL, GL_STATIC_DRAW); - if (gl.version >= 3.0) + if (!gl.legacyMode) return (unsigned int*)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, size * sizeof(unsigned int), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); else return (unsigned int*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); @@ -251,7 +251,7 @@ unsigned int FModelVertexBuffer::SetupFrame(unsigned int frame1, unsigned int fr glBindBuffer(GL_ARRAY_BUFFER, vbo_id); if (vbo_id > 0) { - if (gl.glslversion > 0) + if (!gl.legacyMode) { glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FModelVertex), &VMO[frame1].x); glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FModelVertex), &VMO[frame1].u); diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 3a83f4caf..047153554 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -126,7 +126,7 @@ void FGLRenderer::RenderScreenQuad() void FGLRenderer::BloomScene() { // Only bloom things if enabled and no special fixed light mode is active - if (!gl_bloom || !FGLRenderBuffers::IsEnabled() || gl_fixedcolormap != CM_DEFAULT) + if (!gl_bloom || gl_fixedcolormap != CM_DEFAULT) return; FGLDebug::PushGroup("BloomScene"); @@ -212,7 +212,7 @@ void FGLRenderer::BloomScene() void FGLRenderer::TonemapScene() { - if (gl_tonemap == 0 || !FGLRenderBuffers::IsEnabled()) + if (gl_tonemap == 0) return; FGLDebug::PushGroup("TonemapScene"); @@ -292,7 +292,7 @@ void FGLRenderer::ClearTonemapPalette() void FGLRenderer::LensDistortScene() { - if (gl_lens == 0 || !FGLRenderBuffers::IsEnabled()) + if (gl_lens == 0) return; FGLDebug::PushGroup("LensDistortScene"); diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index fd98522d8..f8b5dfcdd 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -218,17 +218,8 @@ void FGLRenderBuffers::CreateScene(int width, int height, int samples) if (samples > 1) mSceneMultisample = CreateRenderBuffer("SceneMultisample", GetHdrFormat(), samples, width, height); - if ((gl.flags & RFL_NO_DEPTHSTENCIL) != 0) - { - mSceneDepth = CreateRenderBuffer("SceneDepth", GL_DEPTH_COMPONENT24, samples, width, height); - mSceneStencil = CreateRenderBuffer("SceneStencil", GL_STENCIL_INDEX8, samples, width, height); - mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepth, mSceneStencil, samples > 1); - } - else - { - mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, samples, width, height); - mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1); - } + mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, samples, width, height); + mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1); } //========================================================================== @@ -288,7 +279,7 @@ void FGLRenderBuffers::CreateBloom(int width, int height) GLuint FGLRenderBuffers::GetHdrFormat() { - return ((gl.flags & RFL_NO_RGBA16F) != 0) ? GL_RGBA8 : GL_RGBA16F; + return GL_RGBA16F; } //========================================================================== @@ -559,7 +550,7 @@ void FGLRenderBuffers::BindOutputFB() bool FGLRenderBuffers::IsEnabled() { - return gl_renderbuffers && gl.glslversion != 0 && !FailedCreate; + return gl_renderbuffers && !gl.legacyMode && !FailedCreate; } bool FGLRenderBuffers::FailedCreate = false; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index c8f2eb224..20126ba77 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -129,8 +129,8 @@ void FGLRenderer::Initialize(int width, int height) mPresentShader = new FPresentShader(); m2DDrawer = new F2DDrawer; - // Only needed for the core profile, because someone decided it was a good idea to remove the default VAO. - if (gl.buffermethod != BM_CLIENTARRAY) + // needed for the core profile, because someone decided it was a good idea to remove the default VAO. + if (!gl.legacyMode) { glGenVertexArrays(1, &mVAOID); glBindVertexArray(mVAOID); @@ -145,7 +145,7 @@ void FGLRenderer::Initialize(int width, int height) mVBO = new FFlatVertexBuffer(width, height); mSkyVBO = new FSkyVertexBuffer; - if (gl.lightmethod != LM_SOFTWARE) mLights = new FLightBuffer(); + if (!gl.legacyMode) mLights = new FLightBuffer(); else mLights = NULL; gl_RenderState.SetVertexBuffer(mVBO); mFBID = 0; diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 7fda92428..caf276d37 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -314,7 +314,7 @@ void FRenderState::Apply() else mVertexBuffer->BindVBO(); mCurrentVertexBuffer = mVertexBuffer; } - if (gl.glslversion > 0) + if (!gl.legacyMode) { ApplyShader(); } @@ -351,7 +351,7 @@ void FRenderState::ApplyMatrices() void FRenderState::ApplyLightIndex(int index) { - if (gl.lightmethod != LM_SOFTWARE) + if (!gl.legacyMode) { if (index > -1 && GLRenderer->mLights->GetBufferType() == GL_UNIFORM_BUFFER) { diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index 3e0729bbe..6acf44026 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -113,7 +113,7 @@ public: // Without shaders this translation must be applied to any texture. if (alphatexture) { - if (mat->tex->UseBasePalette() || gl.glslversion == 0) translation = TRANSLATION(TRANSLATION_Standard, 8); + if (mat->tex->UseBasePalette() || gl.legacyMode) translation = TRANSLATION(TRANSLATION_Standard, 8); } mEffectState = overrideshader >= 0? overrideshader : mat->mShaderIndex; mShaderTimer = mat->tex->gl_info.shaderspeed; diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index d5ed62755..c34d79816 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -322,7 +322,7 @@ void GLDrawList::SortWallIntoPlane(SortNode * head,SortNode * sort) AddWall(&w); // Splitting is done in the shader with clip planes, if available - if (gl.glslversion < 1.3f) + if (gl.flags & RFL_NO_CLIP_PLANES) { GLWall * ws1; ws->vertcount = 0; // invalidate current vertices. @@ -382,7 +382,7 @@ void GLDrawList::SortSpriteIntoPlane(SortNode * head,SortNode * sort) AddSprite(&s); // add a copy to avoid reallocation issues. // Splitting is done in the shader with clip planes, if available - if (gl.glslversion < 1.3f) + if (gl.flags & RFL_NO_CLIP_PLANES) { GLSprite * ss1; ss1=&sprites[sprites.Size()-1]; @@ -480,6 +480,11 @@ void GLDrawList::SortWallIntoWall(SortNode * head,SortNode * sort) ws1->ztop[0]=ws->ztop[1]=izt; ws1->zbottom[0]=ws->zbottom[1]=izb; ws1->tcs[GLWall::LOLFT].u = ws1->tcs[GLWall::UPLFT].u = ws->tcs[GLWall::LORGT].u = ws->tcs[GLWall::UPRGT].u = iu; + if (gl.buffermethod == BM_DEFERRED) + { + ws->MakeVertices(false); + ws1->MakeVertices(false); + } SortNode * sort2=SortNodes.GetNew(); memset(sort2,0,sizeof(SortNode)); @@ -806,17 +811,19 @@ void GLDrawList::DrawSorted() if (!sorted) { + GLRenderer->mVBO->Map(); MakeSortList(); sorted=DoSort(SortNodes[SortNodeStart]); + GLRenderer->mVBO->Unmap(); } gl_RenderState.ClearClipSplit(); - if (gl.glslversion >= 1.3f) + if (!(gl.flags & RFL_NO_CLIP_PLANES)) { glEnable(GL_CLIP_DISTANCE1); glEnable(GL_CLIP_DISTANCE2); } DoDrawSorted(sorted); - if (gl.glslversion >= 1.3f) + if (!(gl.flags & RFL_NO_CLIP_PLANES)) { glDisable(GL_CLIP_DISTANCE1); glDisable(GL_CLIP_DISTANCE2); @@ -995,7 +1002,7 @@ static FDrawInfoList di_list; FDrawInfo::FDrawInfo() { next = NULL; - if (gl.lightmethod == LM_SOFTWARE) + if (gl.legacyMode) { dldrawlists = new GLDrawList[GLLDL_TYPES]; } diff --git a/src/gl/scene/gl_drawinfo.h b/src/gl/scene/gl_drawinfo.h index 124440eee..0e7296ee3 100644 --- a/src/gl/scene/gl_drawinfo.h +++ b/src/gl/scene/gl_drawinfo.h @@ -30,11 +30,9 @@ enum DLDrawListType { // These are organized so that the various multipass rendering modes have to be set as few times as possible GLLDL_WALLS_PLAIN, // dynamic lights on normal walls - GLLDL_WALLS_BRIGHT, // dynamic lights on brightmapped walls GLLDL_WALLS_MASKED, // dynamic lights on masked midtextures GLLDL_FLATS_PLAIN, // dynamic lights on normal flats - GLLDL_FLATS_BRIGHT, // dynamic lights on brightmapped flats GLLDL_FLATS_MASKED, // dynamic lights on masked flats GLLDL_WALLS_FOG, // lights on fogged walls diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index 8b665648e..1a3fe994b 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -430,7 +430,7 @@ void GLFlat::Draw(int pass, bool trans) // trans only has meaning for GLPASS_LIG { gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false); gl_SetPlaneTextureRotation(&plane, gltexture); - DrawSubsectors(pass, gl.lightmethod != LM_SOFTWARE, true); + DrawSubsectors(pass, !gl.legacyMode, true); gl_RenderState.EnableTextureMatrix(false); } if (renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -468,7 +468,7 @@ inline void GLFlat::PutFlat(bool fog) { Colormap.Clear(); } - if (gl.lightmethod == LM_SOFTWARE) + if (gl.legacyMode) { if (PutFlatCompat(fog)) return; } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index c2812db0f..7d634054a 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -409,7 +409,7 @@ 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) + if (gl.legacyMode) { // also process the render lists with walls and dynamic lights gl_drawinfo->dldrawlists[GLLDL_WALLS_PLAIN].DrawDecals(); @@ -681,7 +681,7 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) gl_RenderState.SetSoftLightLevel(-1); DrawTargeterSprites(); DrawBlend(viewsector); - if (gl.glslversion == 0.0) + if (gl.legacyMode) { gl_RenderState.SetFixedColormap(cm); gl_RenderState.DrawColormapOverlay(); @@ -846,9 +846,9 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo ProcessScene(toscreen); if (mainview && toscreen) EndDrawScene(retval); // do not call this for camera textures. - if (mainview) + if (mainview && FGLRenderBuffers::IsEnabled()) { - if (FGLRenderBuffers::IsEnabled()) mBuffers->BlitSceneToTexture(); + mBuffers->BlitSceneToTexture(); BloomScene(); TonemapScene(); LensDistortScene(); @@ -896,7 +896,7 @@ void FGLRenderer::RenderView (player_t* player) P_FindParticleSubsectors (); - if (gl.lightmethod != LM_SOFTWARE) GLRenderer->mLights->Clear(); + if (!gl.legacyMode) 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; @@ -951,7 +951,7 @@ void FGLRenderer::WriteSavePic (player_t *player, FILE *file, int width, int hei SetFixedColormap(player); gl_RenderState.SetVertexBuffer(mVBO); GLRenderer->mVBO->Reset(); - if (gl.lightmethod != LM_SOFTWARE) GLRenderer->mLights->Clear(); + if (!gl.legacyMode) 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_skydome.cpp b/src/gl/scene/gl_skydome.cpp index 2b6843bdf..97c5cb458 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -87,7 +87,7 @@ FSkyVertexBuffer::~FSkyVertexBuffer() void FSkyVertexBuffer::BindVBO() { glBindBuffer(GL_ARRAY_BUFFER, vbo_id); - if (gl.glslversion > 0) + if (!gl.legacyMode) { glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FSkyVertex), &VSO->x); glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FSkyVertex), &VSO->u); @@ -321,7 +321,7 @@ void FSkyVertexBuffer::RenderDome(FMaterial *tex, int mode) RenderRow(GL_TRIANGLE_FAN, rc); gl_RenderState.EnableTexture(true); // The color array can only be activated now if this is drawn without shader - if (gl.glslversion == 0) + if (gl.legacyMode) { glEnableClientState(GL_COLOR_ARRAY); } diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 0c5b2581a..8a8c89c80 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -870,7 +870,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) RenderStyle.CheckFuzz(); if (RenderStyle.BlendOp == STYLEOP_Fuzz) { - if (gl_fuzztype != 0 && gl.glslversion > 0) + if (gl_fuzztype != 0 && !gl.legacyMode) { // Todo: implement shader selection here RenderStyle = LegacyRenderStyles[STYLE_Translucent]; diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp index 5b2271433..f8bb2a876 100644 --- a/src/gl/scene/gl_walls.cpp +++ b/src/gl/scene/gl_walls.cpp @@ -140,7 +140,7 @@ void GLWall::PutWall(bool translucent) } else { - if (gl.lightmethod == LM_SOFTWARE && !translucent) + if (gl.legacyMode && !translucent) { if (PutWallCompat(passflag[type])) return; } diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 585ced1a8..40145abc4 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -216,9 +216,8 @@ void GLWall::RenderWall(int textured) } else if (vertcount == 0) { - // in case we get here without valid vertex data and no ability to create them now, - // use the quad drawer as fallback (without edge splitting.) - // This can only happen in one special situation, when a translucent line got split during sorting. + // This should never happen but in case it actually does, use the quad drawer as fallback (without edge splitting.) + // This way it at least gets drawn. FQuadDrawer qd; qd.Set(0, glseg.x1, zbottom[0], glseg.y1, tcs[LOLFT].u, tcs[LOLFT].v); qd.Set(1, glseg.x1, ztop[0], glseg.y1, tcs[UPLFT].u, tcs[UPLFT].v); @@ -242,7 +241,7 @@ void GLWall::RenderFogBoundary() { if (gl_fogmode && gl_fixedcolormap == 0) { - if (gl.glslversion > 0.f) + if (!gl.legacyMode) { int rel = rellight + getExtraLight(); gl_SetFog(lightlevel, rel, &Colormap, false); @@ -276,7 +275,7 @@ void GLWall::RenderMirrorSurface() Vector v(glseg.y2-glseg.y1, 0 ,-glseg.x2+glseg.x1); v.Normalize(); - if (gl.glslversion >= 0.f) + if (!gl.legacyMode) { // we use texture coordinates and texture matrix to pass the normal stuff to the shader so that the default vertex buffer format can be used as is. tcs[LOLFT].u = tcs[LORGT].u = tcs[UPLFT].u = tcs[UPRGT].u = v.X(); diff --git a/src/gl/shaders/gl_blurshader.cpp b/src/gl/shaders/gl_blurshader.cpp index 6154c17ce..bf2e29c7e 100644 --- a/src/gl/shaders/gl_blurshader.cpp +++ b/src/gl/shaders/gl_blurshader.cpp @@ -87,20 +87,6 @@ void FBlurShader::Blur(FGLRenderer *renderer, float blurAmount, int sampleCount, else setup->HorizontalShader->Bind(); - if (gl.glslversion < 1.3) - { - if (vertical) - { - setup->VerticalScaleX.Set(1.0f / width); - setup->VerticalScaleY.Set(1.0f / height); - } - else - { - setup->HorizontalScaleX.Set(1.0f / width); - setup->HorizontalScaleY.Set(1.0f / height); - } - } - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, inputTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -156,14 +142,6 @@ FBlurShader::BlurSetup *FBlurShader::GetSetup(float blurAmount, int sampleCount) blurSetup.HorizontalShader->Bind(); glUniform1i(glGetUniformLocation(*blurSetup.HorizontalShader.get(), "SourceTexture"), 0); - if (gl.glslversion < 1.3) - { - blurSetup.VerticalScaleX.Init(*blurSetup.VerticalShader.get(), "ScaleX"); - blurSetup.VerticalScaleY.Init(*blurSetup.VerticalShader.get(), "ScaleY"); - blurSetup.HorizontalScaleX.Init(*blurSetup.HorizontalShader.get(), "ScaleX"); - blurSetup.HorizontalScaleY.Init(*blurSetup.HorizontalShader.get(), "ScaleY"); - } - mBlurSetups.Push(blurSetup); return &mBlurSetups[mBlurSetups.Size() - 1]; diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 8ac2b7093..3c574bd62 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -91,40 +91,27 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * // FString vp_comb; - if (gl.lightmethod == LM_SOFTWARE) + assert(GLRenderer->mLights != NULL); + // On the shader side there is no difference between LM_DEFERRED and LM_DIRECT, it only decides how the buffer is initialized. + unsigned int lightbuffertype = GLRenderer->mLights->GetBufferType(); + unsigned int lightbuffersize = GLRenderer->mLights->GetBlockSize(); + if (lightbuffertype == GL_UNIFORM_BUFFER) { - if (gl.glslversion >= 1.3) + // This differentiation is for some Intel drivers which fail on #extension, so use of #version 140 is necessary + if (gl.glslversion < 1.4f) { - vp_comb = "#version 130\n"; + vp_comb.Format("#version 130\n#extension GL_ARB_uniform_buffer_object : require\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize); } else { - vp_comb = "#define GLSL12_COMPATIBLE\n"; + vp_comb.Format("#version 140\n#define NUM_UBO_LIGHTS %d\n", lightbuffersize); } } else { - 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 = "#version 400 core\n#extension GL_ARB_shader_storage_buffer_object : require\n#define SHADER_STORAGE_LIGHTS\n"; } + if (gl.buffermethod == BM_DEFERRED) { vp_comb << "#define USE_QUAD_DRAWER\n"; @@ -169,12 +156,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * } } - if (gl.glslversion < 1.3) - { - FShaderProgram::PatchVertShader(vp_comb); - FShaderProgram::PatchFragShader(fp_comb); - } - else if (gl.flags & RFL_NO_CLIP_PLANES) + if (gl.flags & RFL_NO_CLIP_PLANES) { // On ATI's GL3 drivers we have to disable gl_ClipDistance because it's hopelessly broken. // This will cause some glitches and regressions but is the only way to avoid total display garbage. @@ -273,7 +255,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * texcoordmatrix_index = glGetUniformLocation(hShader, "uQuadTexCoords"); quadmode_index = glGetUniformLocation(hShader, "uQuadMode"); - if (LM_SOFTWARE != gl.lightmethod && !(gl.flags & RFL_SHADER_STORAGE_BUFFER)) + if (!gl.legacyMode && !(gl.flags & RFL_SHADER_STORAGE_BUFFER)) { int tempindex = glGetUniformBlockIndex(hShader, "LightBufferUBO"); if (tempindex != -1) glUniformBlockBinding(hShader, tempindex, LIGHTBUF_BINDINGPOINT); @@ -424,7 +406,7 @@ static const FEffectShader effectshaders[]= FShaderManager::FShaderManager() { - if (gl.glslversion > 0) CompileShaders(); + if (!gl.legacyMode) CompileShaders(); } //========================================================================== @@ -435,7 +417,7 @@ FShaderManager::FShaderManager() FShaderManager::~FShaderManager() { - if (gl.glslversion > 0) Clean(); + if (!gl.legacyMode) Clean(); } //========================================================================== @@ -577,7 +559,7 @@ EXTERN_CVAR(Int, gl_fuzztype) void FShaderManager::ApplyMatrices(VSMatrix *proj, VSMatrix *view) { - if (gl.glslversion == 0) + if (gl.legacyMode) { glMatrixMode(GL_PROJECTION); glLoadMatrixf(proj->get()); diff --git a/src/gl/shaders/gl_shaderprogram.cpp b/src/gl/shaders/gl_shaderprogram.cpp index 959315a9d..80a7ea99e 100644 --- a/src/gl/shaders/gl_shaderprogram.cpp +++ b/src/gl/shaders/gl_shaderprogram.cpp @@ -240,24 +240,13 @@ FString FShaderProgram::PatchShader(ShaderType type, const FString &code, const if (defines) patchedCode << defines; - if (gl.glslversion >= 1.3) - { - // these settings are actually pointless but there seem to be some old ATI drivers that fail to compile the shader without setting the precision here. - patchedCode << "precision highp int;\n"; - patchedCode << "precision highp float;\n"; - } + // these settings are actually pointless but there seem to be some old ATI drivers that fail to compile the shader without setting the precision here. + patchedCode << "precision highp int;\n"; + patchedCode << "precision highp float;\n"; patchedCode << "#line 1\n"; patchedCode << code; - if (gl.glslversion < 1.3) - { - if (type == Vertex) - PatchVertShader(patchedCode); - else if (type == Fragment) - PatchFragShader(patchedCode); - } - return patchedCode; } @@ -268,36 +257,3 @@ FString FShaderProgram::PatchShader(ShaderType type, const FString &code, const // //========================================================================== -void FShaderProgram::PatchCommon(FString &code) -{ - code.Substitute("precision highp int;", ""); - code.Substitute("precision highp float;", ""); -} - -void FShaderProgram::PatchVertShader(FString &code) -{ - PatchCommon(code); - code.Substitute("in vec", "attribute vec"); - code.Substitute("in float", "attribute float"); - code.Substitute("out vec", "varying vec"); - code.Substitute("out float", "varying float"); - code.Substitute("gl_ClipDistance", "//"); -} - -void FShaderProgram::PatchFragShader(FString &code) -{ - PatchCommon(code); - code.Substitute("out vec4 FragColor;", ""); - code.Substitute("FragColor", "gl_FragColor"); - code.Substitute("in vec", "varying vec"); - // this patches the switch statement to if's. - code.Substitute("break;", ""); - code.Substitute("switch (uFixedColormap)", "int i = uFixedColormap;"); - code.Substitute("case 0:", "if (i == 0)"); - code.Substitute("case 1:", "else if (i == 1)"); - code.Substitute("case 2:", "else if (i == 2)"); - code.Substitute("case 3:", "else if (i == 3)"); - code.Substitute("case 4:", "else if (i == 4)"); - code.Substitute("case 5:", "else if (i == 5)"); - code.Substitute("texture(", "texture2D("); -} diff --git a/src/gl/shaders/gl_shaderprogram.h b/src/gl/shaders/gl_shaderprogram.h index 64a36db49..aabfb1f6f 100644 --- a/src/gl/shaders/gl_shaderprogram.h +++ b/src/gl/shaders/gl_shaderprogram.h @@ -26,16 +26,11 @@ public: operator GLuint() const { return mProgram; } explicit operator bool() const { return mProgram != 0; } - // Needed by FShader - static void PatchVertShader(FString &code); - static void PatchFragShader(FString &code); - private: FShaderProgram(const FShaderProgram &) = delete; FShaderProgram &operator=(const FShaderProgram &) = delete; static FString PatchShader(ShaderType type, const FString &code, const char *defines, int maxGlslVersion); - static void PatchCommon(FString &code); void CreateShader(ShaderType type); FString GetShaderInfoLog(GLuint handle); diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index ec77cfd14..158d7f333 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -162,7 +162,7 @@ void OpenGLFrameBuffer::InitializeState() glEnable(GL_BLEND); glEnable(GL_DEPTH_CLAMP); glDisable(GL_DEPTH_TEST); - if (gl.glslversion == 0) glEnable(GL_TEXTURE_2D); + if (gl.legacyMode) glEnable(GL_TEXTURE_2D); glDisable(GL_LINE_SMOOTH); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/gl/system/gl_interface.cpp b/src/gl/system/gl_interface.cpp index c54d96448..9185b63dc 100644 --- a/src/gl/system/gl_interface.cpp +++ b/src/gl/system/gl_interface.cpp @@ -154,10 +154,10 @@ void gl_LoadExtensions() else Printf("Emulating OpenGL v %s\n", version); } - gl.version = strtod(version, NULL) + 0.01f; + float gl_version = (float)strtod(version, NULL) + 0.01f; - // Don't even start if it's lower than 2.0 or no framebuffers are available - if ((gl.version < 2.0 || !CheckExtension("GL_EXT_framebuffer_object")) && gl.version < 3.0) + // Don't even start if it's lower than 2.0 or no framebuffers are available (The framebuffer extension is needed for glGenerateMipmapsEXT!) + if ((gl_version < 2.0f || !CheckExtension("GL_EXT_framebuffer_object")) && gl_version < 3.0f) { I_FatalError("Unsupported OpenGL version.\nAt least OpenGL 2.0 with framebuffer support is required to run " GAMENAME ".\n"); } @@ -166,105 +166,86 @@ void gl_LoadExtensions() gl.glslversion = strtod((char*)glGetString(GL_SHADING_LANGUAGE_VERSION), NULL) + 0.01f; gl.vendorstring = (char*)glGetString(GL_VENDOR); - gl.lightmethod = LM_SOFTWARE; - gl.buffermethod = BM_CLIENTARRAY; - if ((gl.version >= 3.3f || CheckExtension("GL_ARB_sampler_objects")) && !Args->CheckParm("-nosampler")) + // first test for optional features + 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 ((gl_version >= 3.3f || CheckExtension("GL_ARB_sampler_objects")) && !Args->CheckParm("-nosampler")) { 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"))) + // The minimum requirement for the modern render path are GL 3.0 + uniform buffers + if (gl_version < 3.0f || (gl_version < 3.1f && !CheckExtension("GL_ARB_uniform_buffer_object"))) { - gl.lightmethod = LM_DEFERRED; - gl.buffermethod = BM_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("-noshader")/* || gl.glslversion < 1.2f*/) - { - gl.version = 2.11f; + gl.legacyMode = true; + gl.lightmethod = LM_LEGACY; + gl.buffermethod = BM_LEGACY; gl.glslversion = 0; - gl.lightmethod = LM_SOFTWARE; gl.flags |= RFL_NO_CLIP_PLANES; } - else if (gl.version < 3.0f) - { - if (CheckExtension("GL_NV_GPU_shader4") || CheckExtension("GL_EXT_GPU_shader4")) gl.glslversion = 1.21f; // for pre-3.0 drivers that support capable hardware. Needed for Apple. - else - { - gl.buffermethod = BM_CLIENTARRAY; - gl.glslversion = 0; - } - - if (!CheckExtension("GL_EXT_packed_float")) gl.flags |= RFL_NO_RGBA16F; - if (!CheckExtension("GL_EXT_packed_depth_stencil")) gl.flags |= RFL_NO_DEPTHSTENCIL; - gl.flags |= RFL_NO_CLIP_PLANES; - } - else if (gl.version < 4.f) - { -#ifdef _WIN32 - if (strstr(gl.vendorstring, "ATI Tech")) - { - gl.flags |= RFL_NO_CLIP_PLANES; // gl_ClipDistance is horribly broken on ATI GL3 drivers for Windows. - } -#endif - } - else if (gl.version < 4.5f) - { - // don't use GL 4.x features when running in GL 3 emulation mode. - if (CheckExtension("GL_ARB_buffer_storage")) - { - // work around a problem with older AMD drivers: Their implementation of shader storage buffer objects is piss-poor and does not match uniform buffers even closely. - // Recent drivers, GL 4.4 don't have this problem, these can easily be recognized by also supporting the GL_ARB_buffer_storage extension. - if (CheckExtension("GL_ARB_shader_storage_buffer_object")) - { - // Shader storage buffer objects are broken on current Intel drivers. - if (strstr(gl.vendorstring, "Intel") == NULL) - { - gl.flags |= RFL_SHADER_STORAGE_BUFFER; - } - } - gl.flags |= RFL_BUFFER_STORAGE; - gl.lightmethod = LM_DIRECT; - gl.buffermethod = BM_PERSISTENT; - } - else - { - gl.version = 3.3f; - } - } else { - // Assume that everything works without problems on GL 4.5 drivers where these things are core features. - gl.flags |= RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE; - gl.lightmethod = LM_DIRECT; - gl.buffermethod = BM_PERSISTENT; - } + gl.legacyMode = false; + gl.lightmethod = LM_DEFERRED; + gl.buffermethod = BM_DEFERRED; + if (gl_version < 4.f) + { +#ifdef _WIN32 + if (strstr(gl.vendorstring, "ATI Tech")) + { + gl.flags |= RFL_NO_CLIP_PLANES; // gl_ClipDistance is horribly broken on ATI GL3 drivers for Windows. + } +#endif + } + else if (gl_version < 4.5f) + { + // don't use GL 4.x features when running a GL 3.x context. + if (CheckExtension("GL_ARB_buffer_storage")) + { + // work around a problem with older AMD drivers: Their implementation of shader storage buffer objects is piss-poor and does not match uniform buffers even closely. + // Recent drivers, GL 4.4 don't have this problem, these can easily be recognized by also supporting the GL_ARB_buffer_storage extension. + if (CheckExtension("GL_ARB_shader_storage_buffer_object")) + { + // Shader storage buffer objects are broken on current Intel drivers. + if (strstr(gl.vendorstring, "Intel") == NULL) + { + gl.flags |= RFL_SHADER_STORAGE_BUFFER; + } + } + gl.flags |= RFL_BUFFER_STORAGE; + gl.lightmethod = LM_DIRECT; + gl.buffermethod = BM_PERSISTENT; + } + } + else + { + // Assume that everything works without problems on GL 4.5 drivers where these things are core features. + gl.flags |= RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE; + gl.lightmethod = LM_DIRECT; + gl.buffermethod = BM_PERSISTENT; + } - if (gl.version >= 4.3f || CheckExtension("GL_ARB_invalidate_subdata")) gl.flags |= RFL_INVALIDATE_BUFFER; - if (gl.version >= 4.3f || CheckExtension("GL_KHR_debug")) gl.flags |= RFL_DEBUG; + if (gl_version >= 4.3f || CheckExtension("GL_ARB_invalidate_subdata")) gl.flags |= RFL_INVALIDATE_BUFFER; + if (gl_version >= 4.3f || CheckExtension("GL_KHR_debug")) gl.flags |= RFL_DEBUG; - 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; - } + const char *lm = Args->CheckValue("-lightmethod"); + if (lm != NULL) + { + if (!stricmp(lm, "deferred") && gl.lightmethod == LM_DIRECT) gl.lightmethod = LM_DEFERRED; + } - lm = Args->CheckValue("-buffermethod"); - if (lm != NULL) - { - if (!stricmp(lm, "deferred") && gl.buffermethod == BM_PERSISTENT) gl.buffermethod = BM_DEFERRED; - if (!stricmp(lm, "clientarray")) gl.buffermethod = BM_CLIENTARRAY; + lm = Args->CheckValue("-buffermethod"); + if (lm != NULL) + { + if (!stricmp(lm, "deferred") && gl.buffermethod == BM_PERSISTENT) gl.buffermethod = BM_DEFERRED; + } } int v; - if (gl.lightmethod != LM_SOFTWARE && !(gl.flags & RFL_SHADER_STORAGE_BUFFER)) + if (!gl.legacyMode && !(gl.flags & RFL_SHADER_STORAGE_BUFFER)) { glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &v); gl.maxuniforms = v; @@ -284,24 +265,27 @@ void gl_LoadExtensions() 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); - FUDGE_FUNC(glCheckFramebufferStatus, EXT); - gl_PatchMenu(); + if (gl.legacyMode) + { + // 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); + FUDGE_FUNC(glCheckFramebufferStatus, EXT); + gl_PatchMenu(); + } } //========================================================================== @@ -313,7 +297,7 @@ void gl_LoadExtensions() void gl_PrintStartupLog() { int v = 0; - if (gl.version >= 3.2) glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &v); + if (!gl.legacyMode) glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &v); Printf ("GL_VENDOR: %s\n", glGetString(GL_VENDOR)); Printf ("GL_RENDERER: %s\n", glGetString(GL_RENDERER)); @@ -332,7 +316,7 @@ void gl_PrintStartupLog() glGetIntegerv(GL_MAX_VARYING_FLOATS, &v); Printf ("Max. varying: %d\n", v); - if (gl.lightmethod != LM_SOFTWARE && !(gl.flags & RFL_SHADER_STORAGE_BUFFER)) + if (!gl.legacyMode && !(gl.flags & RFL_SHADER_STORAGE_BUFFER)) { glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &v); Printf ("Max. uniform block size: %d\n", v); @@ -349,7 +333,7 @@ void gl_PrintStartupLog() } // For shader-less, the special alphatexture translation must be changed to actually set the alpha, because it won't get translated by a shader. - if (gl.glslversion == 0) + if (gl.legacyMode) { FRemapTable *remap = translationtables[TRANSLATION_Standard][8]; for (int i = 0; i < 256; i++) diff --git a/src/gl/system/gl_interface.h b/src/gl/system/gl_interface.h index 8300b9973..edaec58c0 100644 --- a/src/gl/system/gl_interface.h +++ b/src/gl/system/gl_interface.h @@ -21,12 +21,10 @@ enum RenderFlags RFL_BUFFER_STORAGE = 8, RFL_SAMPLER_OBJECTS = 16, - RFL_NO_RGBA16F = 32, - RFL_NO_DEPTHSTENCIL = 64, - RFL_NO_CLIP_PLANES = 128, + RFL_NO_CLIP_PLANES = 32, - RFL_INVALIDATE_BUFFER = 256, - RFL_DEBUG = 512 + RFL_INVALIDATE_BUFFER = 64, + RFL_DEBUG = 128 }; enum TexMode @@ -43,15 +41,15 @@ enum TexMode enum ELightMethod { - LM_SOFTWARE = 0, // multi-pass texturing + LM_LEGACY = 0, // placeholder for legacy mode (textured lights), should not be checked anywhere in the code! LM_DEFERRED = 1, // calculate lights up front in a separate pass LM_DIRECT = 2, // calculate lights on the fly along with the render data }; enum EBufferMethod { - BM_CLIENTARRAY = 0, // use a client array instead of a hardware buffer - BM_DEFERRED = 1, // use a temporarily mapped buffer (only necessary on GL 3.x core profile, i.e. Apple) + BM_LEGACY = 0, // placeholder for legacy mode (client arrays), should not be checked anywhere in the code! + BM_DEFERRED = 1, // use a temporarily mapped buffer, for GL 3.x core profile BM_PERSISTENT = 2 // use a persistently mapped buffer }; @@ -64,10 +62,10 @@ struct RenderContext unsigned int uniformblockalignment; int lightmethod; int buffermethod; - float version; float glslversion; int max_texturesize; char * vendorstring; + bool legacyMode; int MaxLights() const { diff --git a/src/gl/textures/gl_hqresize.cpp b/src/gl/textures/gl_hqresize.cpp index 785f3c6e9..0af793e58 100644 --- a/src/gl/textures/gl_hqresize.cpp +++ b/src/gl/textures/gl_hqresize.cpp @@ -264,7 +264,7 @@ unsigned char *gl_CreateUpsampledTextureBuffer ( const FTexture *inputTexture, u return inputBuffer; // [BB] Don't upsample non-shader handled warped textures. Needs too much memory and time - if (gl.glslversion == 0 && inputTexture->bWarped) + if (gl.legacyMode && inputTexture->bWarped) return inputBuffer; // already scaled? diff --git a/src/gl/textures/gl_material.cpp b/src/gl/textures/gl_material.cpp index 6ba1fe4b0..bc9dc2f52 100644 --- a/src/gl/textures/gl_material.cpp +++ b/src/gl/textures/gl_material.cpp @@ -296,7 +296,7 @@ const FHardwareTexture *FGLTexture::Bind(int texunit, int clampmode, int transla if (translation <= 0) translation = -translation; else { - alphatrans = (gl.glslversion == 0 && translation == TRANSLATION(TRANSLATION_Standard, 8)); + alphatrans = (gl.legacyMode && translation == TRANSLATION(TRANSLATION_Standard, 8)); translation = GLTranslationPalette::GetInternalTranslation(translation); } @@ -307,7 +307,7 @@ const FHardwareTexture *FGLTexture::Bind(int texunit, int clampmode, int transla if (hwtex) { // Texture has become invalid - if ((!tex->bHasCanvas && (!tex->bWarped || gl.glslversion == 0)) && tex->CheckModified()) + if ((!tex->bHasCanvas && (!tex->bWarped || gl.legacyMode)) && tex->CheckModified()) { Clean(true); hwtex = CreateHwTexture(); @@ -325,7 +325,7 @@ const FHardwareTexture *FGLTexture::Bind(int texunit, int clampmode, int transla if (!tex->bHasCanvas) { buffer = CreateTexBuffer(translation, w, h, hirescheck, true, alphatrans); - if (tex->bWarped && gl.glslversion == 0 && w*h <= 256*256) // do not software-warp larger textures, especially on the old systems that still need this fallback. + if (tex->bWarped && gl.legacyMode && w*h <= 256*256) // do not software-warp larger textures, especially on the old systems that still need this fallback. { // need to do software warping FWarpTexture *wt = static_cast(tex); @@ -489,7 +489,7 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) mSpriteU[0] = mSpriteV[0] = 0.f; mSpriteU[1] = mSpriteV[1] = 1.f; - FTexture *basetex = (tx->bWarped && gl.glslversion == 0)? tx : tx->GetRedirect(false); + FTexture *basetex = (tx->bWarped && gl.legacyMode)? tx : tx->GetRedirect(false); // allow the redirect only if the textute is not expanded or the scale matches. if (!expanded || (tx->Scale.X == basetex->Scale.X && tx->Scale.Y == basetex->Scale.Y)) { diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index b10c99a17..95ac874b7 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -51,32 +51,6 @@ vec4 desaturate(vec4 texel) // //=========================================================================== -#ifdef GLSL12_COMPATIBLE -vec4 getTexel(vec2 st) -{ - vec4 texel = texture(tex, st); - - // - // Apply texture modes - // - if (uTextureMode != 0) - { - if (uTextureMode == 1) texel.rgb = vec3(1.0,1.0,1.0); - else if (uTextureMode == 2) texel.a = 1.0; - else if (uTextureMode == 3) texel = vec4(1.0-texel.r, 1.0-texel.b, 1.0-texel.g, texel.a); - else if (uTextureMode == 4) texel = vec4(1.0, 1.0, 1.0, texel.r*texel.a); - else if (uTextureMode == 5) - { - if (st.t < 0.0 || st.t > 1.0) - { - texel.a = 0.0; - } - } - } - texel *= uObjectColor; - return desaturate(texel); -} -#else vec4 getTexel(vec2 st) { vec4 texel = texture(tex, st); @@ -113,7 +87,6 @@ vec4 getTexel(vec2 st) return desaturate(texel); } -#endif //=========================================================================== // diff --git a/wadsrc/static/shaders/glsl/tonemap.fp b/wadsrc/static/shaders/glsl/tonemap.fp index 5a84ca5a4..c33349a38 100644 --- a/wadsrc/static/shaders/glsl/tonemap.fp +++ b/wadsrc/static/shaders/glsl/tonemap.fp @@ -70,17 +70,10 @@ uniform sampler2D PaletteLUT; vec3 Tonemap(vec3 color) { ivec3 c = ivec3(clamp(color.rgb, vec3(0.0), vec3(1.0)) * 255.0 + 0.5); -#if __VERSION__ < 130 - int index = (c.r / 4 * 64 + c.g / 4) * 64 + c.b / 4; - float tx = mod(index, 512) / 512.0; - float ty = float(index / 512) / 512.0; - return texture2D(PaletteLUT, vec2(tx, ty)).rgb; -#else int index = ((c.r >> 2) * 64 + (c.g >> 2)) * 64 + (c.b >> 2); int tx = index % 512; int ty = index / 512; return texelFetch(PaletteLUT, ivec2(tx, ty), 0).rgb; -#endif } #else From 7efae2c8f8c5940d2bfb27047e6f87f1c1249088 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 1 Sep 2016 12:14:20 +0200 Subject: [PATCH 05/35] - fixed: When requesting GL version 2.x, do not try to create a core profile context, because that can not support legacy features. --- src/win32/win32gliface.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/win32/win32gliface.cpp b/src/win32/win32gliface.cpp index 494d94233..7ca001e1e 100644 --- a/src/win32/win32gliface.cpp +++ b/src/win32/win32gliface.cpp @@ -748,11 +748,9 @@ bool Win32GLVideo::InitHardware (HWND Window, int multisample) } int prof = WGL_CONTEXT_CORE_PROFILE_BIT_ARB; - const char *lm = Args->CheckValue("-buffermethod"); - if (lm != NULL) - { - if (!stricmp(lm, "clientarray")) prof = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; - } + const char *version = Args->CheckValue("-glversion"); + + if (version != nullptr && strcmp(version, "3.0") < 0) prof = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; for (; prof <= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; prof++) { From 589936f570b43a75df8c44892e1fdd917956e915 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 1 Sep 2016 17:14:51 +0200 Subject: [PATCH 06/35] - draw the colormap blend after postprocessing, not before it. - added colormap shader to postprocessing. This replaces the in-place application of fullscreen colormaps if renderbuffers are active. This way the fully composed scene gets inverted, not each element on its own which is highly problematic for additively blended things. --- src/CMakeLists.txt | 1 + src/gl/compatibility/gl_20.cpp | 57 +++++++++++----------- src/gl/renderer/gl_lightdata.cpp | 2 +- src/gl/renderer/gl_postprocess.cpp | 33 +++++++++++++ src/gl/renderer/gl_renderbuffers.cpp | 9 +++- src/gl/renderer/gl_renderer.cpp | 26 +++++++--- src/gl/renderer/gl_renderer.h | 3 ++ src/gl/renderer/gl_renderstate.cpp | 24 ++++++--- src/gl/scene/gl_scene.cpp | 19 +++++--- src/gl/shaders/gl_colormapshader.cpp | 67 ++++++++++++++++++++++++++ src/gl/shaders/gl_colormapshader.h | 20 ++++++++ wadsrc/static/shaders/glsl/colormap.fp | 16 ++++++ 12 files changed, 224 insertions(+), 53 deletions(-) create mode 100644 src/gl/shaders/gl_colormapshader.cpp create mode 100644 src/gl/shaders/gl_colormapshader.h create mode 100644 wadsrc/static/shaders/glsl/colormap.fp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 03f3372f0..dfac22fe0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1114,6 +1114,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_presentshader.cpp gl/shaders/gl_bloomshader.cpp gl/shaders/gl_blurshader.cpp + gl/shaders/gl_colormapshader.cpp gl/shaders/gl_tonemapshader.cpp gl/shaders/gl_lensshader.cpp gl/system/gl_interface.cpp diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp index 97abc0d82..fdda99130 100644 --- a/src/gl/compatibility/gl_20.cpp +++ b/src/gl/compatibility/gl_20.cpp @@ -66,42 +66,39 @@ void gl_PatchMenu() { - if (gl.legacyMode) + // Radial fog and Doom lighting are not available without full shader support. + + FOptionValues **opt = OptionValues.CheckKey("LightingModes"); + if (opt != NULL) { - // Radial fog and Doom lighting are not available without full shader support. - - FOptionValues **opt = OptionValues.CheckKey("LightingModes"); - if (opt != NULL) + for(int i = (*opt)->mValues.Size()-1; i>=0; i--) { - for(int i = (*opt)->mValues.Size()-1; i>=0; i--) + // Delete 'Doom' lighting mode + if ((*opt)->mValues[i].Value == 2.0 || (*opt)->mValues[i].Value == 8.0) { - // Delete 'Doom' lighting mode - if ((*opt)->mValues[i].Value == 2.0 || (*opt)->mValues[i].Value == 8.0) - { - (*opt)->mValues.Delete(i); - } + (*opt)->mValues.Delete(i); } } - - opt = OptionValues.CheckKey("FogMode"); - if (opt != NULL) - { - for(int i = (*opt)->mValues.Size()-1; i>=0; i--) - { - // Delete 'Radial' fog mode - if ((*opt)->mValues[i].Value == 2.0) - { - (*opt)->mValues.Delete(i); - } - } - } - - // disable features that don't work without shaders. - if (gl_lightmode == 2 || gl_lightmode == 8) gl_lightmode = 3; - if (gl_fogmode == 2) gl_fogmode = 1; - - // todo: remove more unsupported stuff like postprocessing options. } + + opt = OptionValues.CheckKey("FogMode"); + if (opt != NULL) + { + for(int i = (*opt)->mValues.Size()-1; i>=0; i--) + { + // Delete 'Radial' fog mode + if ((*opt)->mValues[i].Value == 2.0) + { + (*opt)->mValues.Delete(i); + } + } + } + + // disable features that don't work without shaders. + if (gl_lightmode == 2 || gl_lightmode == 8) gl_lightmode = 3; + if (gl_fogmode == 2) gl_fogmode = 1; + + // todo: remove more unsupported stuff like postprocessing options. } diff --git a/src/gl/renderer/gl_lightdata.cpp b/src/gl/renderer/gl_lightdata.cpp index 5d153894f..c2329879c 100644 --- a/src/gl/renderer/gl_lightdata.cpp +++ b/src/gl/renderer/gl_lightdata.cpp @@ -71,7 +71,7 @@ CUSTOM_CVAR(Bool, gl_enhanced_nightvision, true, CVAR_ARCHIVE|CVAR_NOINITCALL) { // The fixed colormap state needs to be reset because if this happens when // a shader is set to CM_LITE or CM_TORCH it won't register the change in behavior caused by this CVAR. - if (GLRenderer != NULL && GLRenderer->mShaderManager != NULL) + if (GLRenderer != nullptr && GLRenderer->mShaderManager != nullptr) { GLRenderer->mShaderManager->ResetFixedColormap(); } diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 047153554..77ee5cd79 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -71,6 +71,7 @@ #include "gl/shaders/gl_bloomshader.h" #include "gl/shaders/gl_blurshader.h" #include "gl/shaders/gl_tonemapshader.h" +#include "gl/shaders/gl_colormapshader.h" #include "gl/shaders/gl_lensshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/renderer/gl_2ddrawer.h" @@ -284,6 +285,38 @@ void FGLRenderer::ClearTonemapPalette() mTonemapPalette = nullptr; } +//----------------------------------------------------------------------------- +// +// Colormap scene texture and place the result in the HUD/2D texture +// +//----------------------------------------------------------------------------- + +void FGLRenderer::ColormapScene() +{ + if (gl_fixedcolormap < CM_FIRSTSPECIALCOLORMAP || gl_fixedcolormap >= CM_MAXCOLORMAP) + return; + + FGLDebug::PushGroup("ColormapScene"); + + FGLPostProcessState savedState; + + mBuffers->BindNextFB(); + mBuffers->BindCurrentTexture(0); + mColormapShader->Bind(); + + FSpecialColormap *scm = &SpecialColormaps[gl_fixedcolormap - CM_FIRSTSPECIALCOLORMAP]; + float m[] = { scm->ColorizeEnd[0] - scm->ColorizeStart[0], + scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f }; + + mColormapShader->MapStart.Set(scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], 0.f); + mColormapShader->MapRange.Set(m); + + RenderScreenQuad(); + mBuffers->NextTexture(); + + FGLDebug::PopGroup(); +} + //----------------------------------------------------------------------------- // // Apply lens distortion and place the result in the HUD/2D texture diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index f8b5dfcdd..35be00abc 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -55,7 +55,14 @@ #include "doomerrors.h" CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -CVAR(Bool, gl_renderbuffers, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +CUSTOM_CVAR(Bool, gl_renderbuffers, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + // this CVAR alters some fixed colormap related settings + if (GLRenderer != nullptr && GLRenderer->mShaderManager != nullptr) + { + //GLRenderer->mShaderManager->ResetFixedColormap(); + } +} //========================================================================== // diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 20126ba77..6cd38390f 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -67,6 +67,7 @@ #include "gl/shaders/gl_bloomshader.h" #include "gl/shaders/gl_blurshader.h" #include "gl/shaders/gl_tonemapshader.h" +#include "gl/shaders/gl_colormapshader.h" #include "gl/shaders/gl_lensshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/textures/gl_texture.h" @@ -97,21 +98,30 @@ CVAR(Bool, gl_scale_viewport, true, 0); FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) { framebuffer = fb; - mClipPortal = NULL; - mCurrentPortal = NULL; + mClipPortal = nullptr; + mCurrentPortal = nullptr; mMirrorCount = 0; mPlaneMirrorCount = 0; mLightCount = 0; mAngles = FRotator(0.f, 0.f, 0.f); mViewVector = FVector2(0,0); - mVBO = NULL; - mSkyVBO = NULL; + mVBO = nullptr; + mSkyVBO = nullptr; gl_spriteindex = 0; - mShaderManager = NULL; - gllight = glpart2 = glpart = mirrortexture = NULL; - mLights = NULL; + mShaderManager = nullptr; + gllight = glpart2 = glpart = mirrortexture = nullptr; + mLights = nullptr; m2DDrawer = nullptr; mTonemapPalette = nullptr; + mBuffers = nullptr; + mPresentShader = nullptr; + mBloomExtractShader = nullptr; + mBloomCombineShader = nullptr; + mBlurShader = nullptr; + mTonemapShader = nullptr; + mTonemapPalette = nullptr; + mColormapShader = nullptr; + mLensShader = nullptr; } void gl_LoadModels(); @@ -124,6 +134,7 @@ void FGLRenderer::Initialize(int width, int height) mBloomCombineShader = new FBloomCombineShader(); mBlurShader = new FBlurShader(); mTonemapShader = new FTonemapShader(); + mColormapShader = new FColormapShader(); mTonemapPalette = nullptr; mLensShader = new FLensShader(); mPresentShader = new FPresentShader(); @@ -184,6 +195,7 @@ FGLRenderer::~FGLRenderer() if (mBlurShader) delete mBlurShader; if (mTonemapShader) delete mTonemapShader; if (mTonemapPalette) delete mTonemapPalette; + if (mColormapShader) delete mColormapShader; if (mLensShader) delete mLensShader; } diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 4b663680f..120f6a449 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -23,6 +23,7 @@ class FBloomExtractShader; class FBloomCombineShader; class FBlurShader; class FTonemapShader; +class FColormapShader; class FLensShader; class FPresentShader; class F2DDrawer; @@ -93,6 +94,7 @@ public: FBloomCombineShader *mBloomCombineShader; FBlurShader *mBlurShader; FTonemapShader *mTonemapShader; + FColormapShader *mColormapShader; FHardwareTexture *mTonemapPalette; FLensShader *mLensShader; FPresentShader *mPresentShader; @@ -166,6 +168,7 @@ public: void EndDrawScene(sector_t * viewsector); void BloomScene(); void TonemapScene(); + void ColormapScene(); void BindTonemapPalette(int texunit); void ClearTonemapPalette(); void LensDistortScene(); diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index caf276d37..c3cc905e5 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -49,6 +49,7 @@ #include "gl/renderer/gl_renderstate.h" #include "gl/renderer/gl_colormap.h" #include "gl/dynlights//gl_lightbuffer.h" +#include "gl/renderer/gl_renderbuffers.h" void gl_SetTextureMode(int type); @@ -220,15 +221,24 @@ bool FRenderState::ApplyShader() { activeShader->muFixedColormap.Set(0); } - else if (mColormapState < CM_MAXCOLORMAP) + else if (mColormapState > CM_DEFAULT && mColormapState < CM_MAXCOLORMAP) { - FSpecialColormap *scm = &SpecialColormaps[gl_fixedcolormap - CM_FIRSTSPECIALCOLORMAP]; - float m[] = { scm->ColorizeEnd[0] - scm->ColorizeStart[0], - scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f }; + if (FGLRenderBuffers::IsEnabled()) + { + // When using postprocessing to apply the colormap, we must render the image fullbright here. + activeShader->muFixedColormap.Set(2); + activeShader->muColormapStart.Set(1, 1, 1, 1.f); + } + else + { + FSpecialColormap *scm = &SpecialColormaps[gl_fixedcolormap - CM_FIRSTSPECIALCOLORMAP]; + float m[] = { scm->ColorizeEnd[0] - scm->ColorizeStart[0], + scm->ColorizeEnd[1] - scm->ColorizeStart[1], scm->ColorizeEnd[2] - scm->ColorizeStart[2], 0.f }; - activeShader->muFixedColormap.Set(1); - activeShader->muColormapStart.Set(scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], 0.f); - activeShader->muColormapRange.Set(m); + activeShader->muFixedColormap.Set(1); + activeShader->muColormapStart.Set(scm->ColorizeStart[0], scm->ColorizeStart[1], scm->ColorizeStart[2], 0.f); + activeShader->muColormapRange.Set(m); + } } else if (mColormapState == CM_FOGLAYER) { diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 7d634054a..ab3fe744d 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -635,9 +635,9 @@ void FGLRenderer::DrawBlend(sector_t * viewsector) V_AddBlend (player->BlendR, player->BlendG, player->BlendB, player->BlendA, blend); } + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (blend[3]>0.0f) { - gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); gl_RenderState.SetColor(blend[0], blend[1], blend[2], blend[3]); gl_FillScreen(); } @@ -676,16 +676,19 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) { DrawPlayerSprites (viewsector, false); } - int cm = gl_RenderState.GetFixedColormap(); + if (gl.legacyMode) + { + int cm = gl_RenderState.GetFixedColormap(); + gl_RenderState.SetFixedColormap(cm); + gl_RenderState.DrawColormapOverlay(); + } + gl_RenderState.SetFixedColormap(CM_DEFAULT); gl_RenderState.SetSoftLightLevel(-1); DrawTargeterSprites(); - DrawBlend(viewsector); - if (gl.legacyMode) + if (FGLRenderBuffers::IsEnabled()) { - gl_RenderState.SetFixedColormap(cm); - gl_RenderState.DrawColormapOverlay(); - gl_RenderState.SetFixedColormap(CM_DEFAULT); + DrawBlend(viewsector); } // Restore standard rendering state @@ -851,7 +854,9 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo mBuffers->BlitSceneToTexture(); BloomScene(); TonemapScene(); + ColormapScene(); LensDistortScene(); + DrawBlend(viewsector); // This should be done after postprocessing, not before. } mDrawingScene2D = false; eye->TearDown(); diff --git a/src/gl/shaders/gl_colormapshader.cpp b/src/gl/shaders/gl_colormapshader.cpp new file mode 100644 index 000000000..f28003150 --- /dev/null +++ b/src/gl/shaders/gl_colormapshader.cpp @@ -0,0 +1,67 @@ +/* +** gl_colormapshader.cpp +** Applies a fullscreen colormap to the scene +** +**--------------------------------------------------------------------------- +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. Full disclosure of the entire project's source code, except for third +** party libraries is mandatory. (NOTE: This clause is non-negotiable!) +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "gl/gl_functions.h" +#include "vectors.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_colormapshader.h" + +void FColormapShader::Bind() +{ + auto &shader = mShader; + if (!shader) + { + shader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + shader.Compile(FShaderProgram::Fragment, "shaders/glsl/colormap.fp", "", 330); + shader.SetFragDataLocation(0, "FragColor"); + shader.Link("shaders/glsl/colormap"); + shader.SetAttribLocation(0, "PositionInProjection"); + MapStart.Init(shader, "uFixedColormapStart"); + MapRange.Init(shader, "uFixedColormapRange"); + } + shader.Bind(); +} + diff --git a/src/gl/shaders/gl_colormapshader.h b/src/gl/shaders/gl_colormapshader.h new file mode 100644 index 000000000..b20c23a4c --- /dev/null +++ b/src/gl/shaders/gl_colormapshader.h @@ -0,0 +1,20 @@ +#ifndef __GL_COLORMAPSHADER_H +#define __GL_COLORMAPSHADER_H + +#include "gl_shaderprogram.h" + +class FColormapShader +{ +public: + void Bind(); + + FBufferedUniformSampler SceneTexture; + FUniform4f MapStart; + FUniform4f MapRange; + +private: + + FShaderProgram mShader; +}; + +#endif \ No newline at end of file diff --git a/wadsrc/static/shaders/glsl/colormap.fp b/wadsrc/static/shaders/glsl/colormap.fp new file mode 100644 index 000000000..e86429c37 --- /dev/null +++ b/wadsrc/static/shaders/glsl/colormap.fp @@ -0,0 +1,16 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D tex; +uniform vec4 uFixedColormapStart; +uniform vec4 uFixedColormapRange; + +void main() +{ + vec4 frag = texture(tex, TexCoord); + float gray = (frag.r * 0.3 + frag.g * 0.56 + frag.b * 0.14); + vec4 cm = uFixedColormapStart + gray * uFixedColormapRange; + FragColor = vec4(clamp(cm.rgb, 0.0, 1.0), frag.a); +} + From 4e8027612ff40ec71d1717171dddb3dc253cb16c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 1 Sep 2016 17:38:17 +0200 Subject: [PATCH 07/35] - restored 2 lines of code that somehow got lost before the last commit. --- src/gl/scene/gl_scene.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index ab3fe744d..c1f022ab0 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -641,6 +641,8 @@ void FGLRenderer::DrawBlend(sector_t * viewsector) gl_RenderState.SetColor(blend[0], blend[1], blend[2], blend[3]); gl_FillScreen(); } + gl_RenderState.ResetColor(); + gl_RenderState.EnableTexture(true); } From c4357bd352b5f8ca0a490a75ae4c6f5ebed77ad5 Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Thu, 1 Sep 2016 13:49:58 -0500 Subject: [PATCH 08/35] Tracer pointer is no longer a safe candidate for storing player morph pointers. Instead, actors must have a new, non-manipulatable pointer. This fixes the following circumstances: - Crashes occurred if a particular actor was a tracer to the player and the actor was not gone by the time the player unmorphs. - Failed unmorphs occur if tracer was manipulated through means like A_RearrangePointers, etc. --- src/actor.h | 1 + src/g_shared/a_morph.cpp | 57 ++++++++++++++++++++++------------------ src/p_mobj.cpp | 5 ++++ src/version.h | 2 +- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/actor.h b/src/actor.h index 154d14289..b895d1e2a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1057,6 +1057,7 @@ public: int skillrespawncount; int TIDtoHate; // TID of things to hate (0 if none) FNameNoInit Species; // For monster families + TObjPtr alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals. TObjPtr tracer; // Thing being chased/attacked for tracers TObjPtr master; // Thing which spawned this one (prevents mutual attacks) double Floorclip; // value to use for floor clipping diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 98af9ff25..57271f768 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -38,7 +38,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp APlayerPawn *actor; actor = p->mo; - if (actor == NULL) + if (actor == nullptr) { return false; } @@ -55,7 +55,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp if ((p->mo->GetClass() == spawntype) && (p->mo->PlayerFlags & PPF_CANSUPERMORPH) && (p->morphTics < (((duration) ? duration : MORPHTICS) - TICRATE)) - && (p->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true) == NULL)) + && (p->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true) == nullptr)) { // Make a super chicken p->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2)); } @@ -65,7 +65,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp { // Dead players cannot morph return false; } - if (spawntype == NULL) + if (spawntype == nullptr) { return false; } @@ -94,7 +94,8 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp } morphed->Angles.Yaw = actor->Angles.Yaw; morphed->target = actor->target; - morphed->tracer = actor; + morphed->tracer = actor->tracer; + morphed->alternative = actor; morphed->FriendPlayer = actor->FriendPlayer; morphed->DesignatedTeam = actor->DesignatedTeam; morphed->Score = actor->Score; @@ -113,7 +114,8 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp morphed->flags2 |= actor->flags2 & MF2_FLY; morphed->flags3 |= actor->flags3 & MF3_GHOST; AActor *eflash = Spawn(((enter_flash) ? enter_flash : RUNTIME_CLASS(ATeleportFog)), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE); - actor->player = NULL; + actor->player = nullptr; + actor->alternative = morphed; actor->flags &= ~(MF_SOLID|MF_SHOOTABLE); actor->flags |= MF_UNMORPHED; actor->renderflags |= RF_INVISIBLE; @@ -129,7 +131,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp p->Vel.X = p->Vel.Y = 0; morphed->ObtainInventory (actor); // Remove all armor - for (item = morphed->Inventory; item != NULL; ) + for (item = morphed->Inventory; item != nullptr; ) { AInventory *next = item->Inventory; if (item->IsKindOf (RUNTIME_CLASS(AArmor))) @@ -182,7 +184,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, // because the level or game is ended while morphed, // by the time it gets executed the morphed player // pawn instance may have already been destroyed. - if (pmo == NULL || pmo->tracer == NULL) + if (pmo == nullptr || pmo->alternative == nullptr) { return false; } @@ -197,7 +199,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, return false; } - mo = barrier_cast(pmo->tracer); + mo = barrier_cast(pmo->alternative); mo->SetOrigin (pmo->Pos(), false); mo->flags |= MF_SOLID; pmo->flags &= ~MF_SOLID; @@ -208,10 +210,14 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, player->morphTics = 2*TICRATE; return false; } - pmo->player = NULL; + // No longer using tracer as morph storage. That is what 'alternative' is for. + // If the tracer has changed on the morph, change the original too. + mo->target = pmo->target; + mo->tracer = pmo->tracer; + pmo->player = nullptr; // Remove the morph power if the morph is being undone prematurely. - for (AInventory *item = pmo->Inventory, *next = NULL; item != NULL; item = next) + for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next) { next = item->Inventory; if (item->IsKindOf(RUNTIME_CLASS(APowerMorph))) @@ -252,10 +258,10 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, player->morphTics = 0; player->MorphedPlayerClass = 0; player->MorphStyle = 0; - player->MorphExitFlash = NULL; + player->MorphExitFlash = nullptr; player->viewheight = mo->ViewHeight; AInventory *level2 = mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true); - if (level2 != NULL) + if (level2 != nullptr) { level2->Destroy (); } @@ -310,31 +316,31 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, } } - AActor *eflash = NULL; - if (exit_flash != NULL) + AActor *eflash = nullptr; + if (exit_flash != nullptr) { eflash = Spawn(exit_flash, pmo->Vec3Angle(20., mo->Angles.Yaw, TELEFOGHEIGHT), ALLOW_REPLACE); if (eflash) eflash->target = mo; } mo->SetupWeaponSlots(); // Use original class's weapon slots. beastweap = player->ReadyWeapon; - if (player->PremorphWeapon != NULL) + if (player->PremorphWeapon != nullptr) { player->PremorphWeapon->PostMorphWeapon (); } else { - player->ReadyWeapon = player->PendingWeapon = NULL; + player->ReadyWeapon = player->PendingWeapon = nullptr; } if (correctweapon) { // Better "lose morphed weapon" semantics PClassActor *morphweapon = PClass::FindActor(pmo->MorphWeapon); - if (morphweapon != NULL && morphweapon->IsDescendantOf(RUNTIME_CLASS(AWeapon))) + if (morphweapon != nullptr && morphweapon->IsDescendantOf(RUNTIME_CLASS(AWeapon))) { AWeapon *OriginalMorphWeapon = static_cast(mo->FindInventory (morphweapon)); - if ((OriginalMorphWeapon != NULL) && (OriginalMorphWeapon->GivenAsMorphWeapon)) + if ((OriginalMorphWeapon != nullptr) && (OriginalMorphWeapon->GivenAsMorphWeapon)) { // You don't get to keep your morphed weapon. - if (OriginalMorphWeapon->SisterWeapon != NULL) + if (OriginalMorphWeapon->SisterWeapon != nullptr) { OriginalMorphWeapon->SisterWeapon->Destroy (); } @@ -344,20 +350,21 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, } else // old behaviour (not really useful now) { // Assumptions made here are no longer valid - if (beastweap != NULL) + if (beastweap != nullptr) { // You don't get to keep your morphed weapon. - if (beastweap->SisterWeapon != NULL) + if (beastweap->SisterWeapon != nullptr) { beastweap->SisterWeapon->Destroy (); } beastweap->Destroy (); } } - pmo->tracer = NULL; + mo->alternative = nullptr; + pmo->alternative = nullptr; pmo->Destroy (); // Restore playerclass armor to its normal amount. AHexenArmor *hxarmor = mo->FindInventory(); - if (hxarmor != NULL) + if (hxarmor != nullptr) { hxarmor->Slots[4] = mo->GetClass()->HexenArmor[0]; } @@ -517,9 +524,9 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor (actor->player->morphTics) && (actor->player->MorphStyle & MORPH_UNDOBYDEATH) && (actor->player->mo) && - (actor->player->mo->tracer)) + (actor->player->mo->alternative)) { - AActor *realme = actor->player->mo->tracer; + AActor *realme = actor->player->mo->alternative; int realstyle = actor->player->MorphStyle; int realhealth = actor->health; if (P_UndoPlayerMorph(actor->player, actor->player, 0, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED))) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index f5791d079..6d8ce4418 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -389,6 +389,11 @@ void AActor::Serialize(FArchive &arc) arc << SpriteRotation; } + if (SaveVersion >= 4550) + { + arc << alternative; + } + { FString tagstr; if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag; diff --git a/src/version.h b/src/version.h index cffa25bdd..57dacaa00 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4549 +#define SAVEVER 4550 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) From bf5f10a89717b9d7b970fc53fc7125ed946c6eb3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 2 Sep 2016 10:48:38 +0200 Subject: [PATCH 09/35] - added all relevant licenses to docs. --- docs/licenses/README.TXT | 27 ++ docs/licenses/bsd.txt | 27 ++ docs/licenses/buildlic.txt | 71 ++++++ docs/licenses/cephes.txt | 10 + docs/licenses/doomlic.txt | 112 +++++++++ docs/licenses/dumb.txt | 54 ++++ docs/licenses/gdtoa.txt | 27 ++ docs/licenses/lgpl.txt | 504 +++++++++++++++++++++++++++++++++++++ 8 files changed, 832 insertions(+) create mode 100644 docs/licenses/README.TXT create mode 100644 docs/licenses/bsd.txt create mode 100644 docs/licenses/buildlic.txt create mode 100644 docs/licenses/cephes.txt create mode 100644 docs/licenses/doomlic.txt create mode 100644 docs/licenses/dumb.txt create mode 100644 docs/licenses/gdtoa.txt create mode 100644 docs/licenses/lgpl.txt diff --git a/docs/licenses/README.TXT b/docs/licenses/README.TXT new file mode 100644 index 000000000..f60796436 --- /dev/null +++ b/docs/licenses/README.TXT @@ -0,0 +1,27 @@ +The original Doom source code was released by id Software under the +Doom Source Code License. See doomlic.txt. + +Parts of the renderer use code from the BUILD engine by Ken Silverman. +See buildlic.txt. + +The majority of original code uses a BSD-like lincese. See bsd.txt. + +This software is based in part on the work of the Independent JPEG Group. + +This software uses the 'zlib' general purpose compression library by +Jean-loup Gailly and Mark Adler. + +This software uses the gdtoa package, see gdtoa.txt. + +This software uses the snes_spc library, which is covered by the GNU Lesser +General Public License. See lgpl.txt. + +This software uses the "Dynamic Universal Music Bibliotheque" library for +MOD music playback. See dumb.txt for original license. The version used, +however, has been heavily modified from its original form and is the same +version used by the foobar2000 component foo_dumb as of mid-2008, found at +http://kode54.foobar2000.org/. + +This software uses the OPL emulator from MAME 0.95. Playback of MUS files +on the OPL emulation is accomplished with some help from Vladimir Arnost's +MUS File Player Library, with fixes to make it more accurate. diff --git a/docs/licenses/bsd.txt b/docs/licenses/bsd.txt new file mode 100644 index 000000000..aa48716a9 --- /dev/null +++ b/docs/licenses/bsd.txt @@ -0,0 +1,27 @@ +**--------------------------------------------------------------------------- +** Copyright 1998-2009 Randy Heit, Christoph Oelckers, et al. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- \ No newline at end of file diff --git a/docs/licenses/buildlic.txt b/docs/licenses/buildlic.txt new file mode 100644 index 000000000..a0cec1251 --- /dev/null +++ b/docs/licenses/buildlic.txt @@ -0,0 +1,71 @@ +BUILD SOURCE CODE LICENSE TERMS: 06/20/2000 + +[1] I give you permission to make modifications to my Build source and + distribute it, BUT: + +[2] Any derivative works based on my Build source may be distributed ONLY + through the INTERNET. + +[3] Distribution of any derivative works MUST be done completely FREE of + charge - no commercial exploitation whatsoever. + +[4] Anything you distribute which uses a part of my Build Engine source + code MUST include: + + [A] The following message somewhere in the archive: + + // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman + // Ken Silverman's official web site: "http://www.advsys.net/ken" + // See the included license file "BUILDLIC.TXT" for license info. + + [B] This text file "BUILDLIC.TXT" along with it. + + [C] Any source files that you modify must include this message as well: + + // This file has been modified from Ken Silverman's original release + +[5] The use of the Build Engine for commercial purposes will require an + appropriate license arrangement with me. Contact information is + on my web site. + +[6] I take no responsibility for damage to your system. + +[7] Technical support: Before contacting me with questions, please read + and do ALL of the following! + + [A] Look though ALL of my text files. There are 7 of them (including this + one). I like to think that I wrote them for a reason. You will find + many of your answers in the history section of BUILD.TXT and + BUILD2.TXT (they're located inside SRC.ZIP). + + [B] If that doesn't satisfy you, then try going to: + + "http://www.advsys.net/ken/buildsrc" + + where I will maintain a Build Source Code FAQ (or perhaps I might + just provide a link to a good FAQ). + + [C] I am willing to respond to questions, but ONLY if they come at a rate + that I can handle. + + PLEASE TRY TO AVOID ASKING DUPLICATE QUESTIONS! + + As my line of defense, I will post my current policy about + answering Build source questions (right below the E-mail address + on my web site.) You can check there to see if I'm getting + overloaded with questions or not. + + If I'm too busy, it might say something like this: + + I'm too busy to answer Build source questions right now. + Sorry, but don't expect a reply from me any time soon. + + If I'm open for Build source questions, please state your question + clearly and don't include any unsolicited attachments unless + they're really small (like less than 50k). Assume that I have + a 28.8k modem. Also, don't leave out important details just + to make your question appear shorter - making me guess what + you're asking doesn't save me time! + +---------------------------------------------------------------------------- +-Ken S. (official web site: http://www.advsys.net/ken) diff --git a/docs/licenses/cephes.txt b/docs/licenses/cephes.txt new file mode 100644 index 000000000..cb4e3f9d6 --- /dev/null +++ b/docs/licenses/cephes.txt @@ -0,0 +1,10 @@ + Some software in this archive may be from the book _Methods and +Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster +International, 1989) or from the Cephes Mathematical Library, a +commercial product. In either event, it is copyrighted by the author. +What you see here may be used freely but it comes with no support or +guarantee. + + Stephen L. Moshier + moshier@na-net.ornl.gov + \ No newline at end of file diff --git a/docs/licenses/doomlic.txt b/docs/licenses/doomlic.txt new file mode 100644 index 000000000..2b2252ee2 --- /dev/null +++ b/docs/licenses/doomlic.txt @@ -0,0 +1,112 @@ + + + LIMITED USE SOFTWARE LICENSE AGREEMENT + + This Limited Use Software License Agreement (the "Agreement") +is a legal agreement between you, the end-user, and Id Software, Inc. +("ID"). By downloading or purchasing the software material, which +includes source code (the "Source Code"), artwork data, music and +software tools (collectively, the "Software"), you are agreeing to +be bound by the terms of this Agreement. If you do not agree to the +terms of this Agreement, promptly destroy the Software you may have +downloaded or copied. + +ID SOFTWARE LICENSE + +1. Grant of License. ID grants to you the right to use the +Software. You have no ownership or proprietary rights in or to the +Software, or the Trademark. For purposes of this section, "use" means +loading the Software into RAM, as well as installation on a hard disk +or other storage device. The Software, together with any archive copy +thereof, shall be destroyed when no longer used in accordance with +this Agreement, or when the right to use the Software is terminated. +You agree that the Software will not be shipped, transferred or +exported into any country in violation of the U.S. Export +Administration Act (or any other law governing such matters) and that +you will not utilize, in any other manner, the Software in violation +of any applicable law. + +2. Permitted Uses. For educational purposes only, you, the +end-user, may use portions of the Source Code, such as particular +routines, to develop your own software, but may not duplicate the +Source Code, except as noted in paragraph 4. The limited right +referenced in the preceding sentence is hereinafter referred to as +"Educational Use." By so exercising the Educational Use right you +shall not obtain any ownership, copyright, proprietary or other +interest in or to the Source Code, or any portion of the Source +Code. You may dispose of your own software in your sole discretion. +With the exception of the Educational Use right, you may not +otherwise use the Software, or an portion of the Software, which +includes the Source Code, for commercial gain. + +3. Prohibited Uses: Under no circumstances shall you, the +end-user, be permitted, allowed or authorized to commercially exploit +the Software. Neither you nor anyone at your direction shall do any +of the following acts with regard to the Software, or any portion +thereof: + + Rent; + + Sell; + + Lease; + + Offer on a pay-per-play basis; + + Distribute for money or any other consideration; or + + In any other manner and through any medium whatsoever +commercially exploit or use for any commercial purpose. + +Notwithstanding the foregoing prohibitions, you may commercially +exploit the software you develop by exercising the Educational Use +right, referenced in paragraph 2. hereinabove. + +4. Copyright. The Software and all copyrights related thereto +(including all characters and other images generated by the Software +or depicted in the Software) are owned by ID and is protected by +United States copyright laws and international treaty provisions. +Id shall retain exclusive ownership and copyright in and to the +Software and all portions of the Software and you shall have no +ownership or other proprietary interest in such materials. You must +treat the Software like any other copyrighted material. You may not +otherwise reproduce, copy or disclose to others, in whole or in any +part, the Software. You may not copy the written materials +accompanying the Software. You agree to use your best efforts to +see that any user of the Software licensed hereunder complies with +this Agreement. + +5. NO WARRANTIES. ID DISCLAIMS ALL WARRANTIES, BOTH EXPRESS +IMPLIED, INCLUDING BUT NOT LIMITED TO, IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE WITH RESPECT +TO THE SOFTWARE. THIS LIMITED WARRANTY GIVES YOU SPECIFIC LEGAL +RIGHTS. YOU MAY HAVE OTHER RIGHTS WHICH VARY FROM JURISDICTION TO +JURISDICTION. ID DOES NOT WARRANT THAT THE OPERATION OF THE SOFTWARE +WILL BE UNINTERRUPTED, ERROR FREE OR MEET YOUR SPECIFIC REQUIREMENTS. +THE WARRANTY SET FORTH ABOVE IS IN LIEU OF ALL OTHER EXPRESS +WARRANTIES WHETHER ORAL OR WRITTEN. THE AGENTS, EMPLOYEES, +DISTRIBUTORS, AND DEALERS OF ID ARE NOT AUTHORIZED TO MAKE +MODIFICATIONS TO THIS WARRANTY, OR ADDITIONAL WARRANTIES ON BEHALF +OF ID. + + Exclusive Remedies. The Software is being offered to you +free of any charge. You agree that you have no remedy against ID, its +affiliates, contractors, suppliers, and agents for loss or damage +caused by any defect or failure in the Software regardless of the form +of action, whether in contract, tort, includinegligence, strict +liability or otherwise, with regard to the Software. This Agreement +shall be construed in accordance with and governed by the laws of the +State of Texas. Copyright and other proprietary matters will be +governed by United States laws and international treaties. IN ANY +CASE, ID SHALL NOT BE LIABLE FOR LOSS OF DATA, LOSS OF PROFITS, LOST +SAVINGS, SPECIAL, INCIDENTAL, CONSEQUENTIAL, INDIRECT OR OTHER +SIMILAR DAMAGES ARISING FROM BREACH OF WARRANTY, BREACH OF CONTRACT, +NEGLIGENCE, OR OTHER LEGAL THEORY EVEN IF ID OR ITS AGENT HAS BEEN +ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY +OTHER PARTY. Some jurisdictions do not allow the exclusion or +limitation of incidental or consequential damages, so the above +limitation or exclusion may not apply to you. + + + + diff --git a/docs/licenses/dumb.txt b/docs/licenses/dumb.txt new file mode 100644 index 000000000..231bfa7f1 --- /dev/null +++ b/docs/licenses/dumb.txt @@ -0,0 +1,54 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * licence.txt - Conditions for use of DUMB. / / \ \ + * | < / \_ + * If you do not agree to these terms, please | \/ /\ / + * do not use DUMB. \_ / > / + * | \ / / + * Information in [brackets] is provided to aid | ' / + * interpretation of the licence. \__/ + */ + + +Dynamic Universal Music Bibliotheque + +Copyright (C) 2001-2003 Ben Davis, Robert J Ohannessian and Julien Cugniere + +This software is provided 'as-is', without any express or implied warranty. +In no event shall the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a + product, you are requested to acknowledge its use in the product + documentation, along with details on where to get an unmodified version of + this software, but this is not a strict requirement. + + [Note that the above point asks for a link to DUMB, not just a mention. + Googling for DUMB doesn't help much! The URL is "http://dumb.sf.net/".] + + [The only reason why the link is not strictly required is that such a + requirement prevents DUMB from being used in projects with certain other + licences, notably the GPL. See http://www.gnu.org/philosophy/bsd.html .] + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed from or altered in any source distribution. + +4. If you are using the Program in someone else's bedroom at any Monday + 3:05 PM, you are not allowed to modify the Program for ten minutes. [This + clause provided by Inphernic; every licence should contain at least one + clause, the reasoning behind which is far from obvious.] diff --git a/docs/licenses/gdtoa.txt b/docs/licenses/gdtoa.txt new file mode 100644 index 000000000..13c0d4140 --- /dev/null +++ b/docs/licenses/gdtoa.txt @@ -0,0 +1,27 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ diff --git a/docs/licenses/lgpl.txt b/docs/licenses/lgpl.txt new file mode 100644 index 000000000..5ab7695ab --- /dev/null +++ b/docs/licenses/lgpl.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + From ee503ea2751255e6b38a89d49e2b61f02c25a8e3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 2 Sep 2016 10:55:56 +0200 Subject: [PATCH 10/35] - removed now redundant mystdint.h file. Visual Studio now ships stdint.h so this workaround is no longer needed. --- src/gl/hqnx/common.h | 2 +- src/gl/hqnx/hq2x.cpp | 1 - src/gl/hqnx/hq3x.cpp | 1 - src/gl/hqnx/hq4x.cpp | 1 - src/gl/hqnx/hqx.h | 2 +- src/gl/hqnx/init.cpp | 1 - src/gl/hqnx/mystdint.h | 19 ------------------- 7 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 src/gl/hqnx/mystdint.h diff --git a/src/gl/hqnx/common.h b/src/gl/hqnx/common.h index 3388651bf..c3e700571 100644 --- a/src/gl/hqnx/common.h +++ b/src/gl/hqnx/common.h @@ -23,7 +23,7 @@ #define __HQX_COMMON_H_ #include -#include "mystdint.h" +#include #define MASK_2 0x0000FF00 #define MASK_13 0x00FF00FF diff --git a/src/gl/hqnx/hq2x.cpp b/src/gl/hqnx/hq2x.cpp index 679a8c2b6..637d1fb78 100644 --- a/src/gl/hqnx/hq2x.cpp +++ b/src/gl/hqnx/hq2x.cpp @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mystdint.h" #include "common.h" #include "hqx.h" diff --git a/src/gl/hqnx/hq3x.cpp b/src/gl/hqnx/hq3x.cpp index cbc7fab39..3768f9c92 100644 --- a/src/gl/hqnx/hq3x.cpp +++ b/src/gl/hqnx/hq3x.cpp @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mystdint.h" #include "common.h" #include "hqx.h" diff --git a/src/gl/hqnx/hq4x.cpp b/src/gl/hqnx/hq4x.cpp index 7ad6e06c3..5af8193cb 100644 --- a/src/gl/hqnx/hq4x.cpp +++ b/src/gl/hqnx/hq4x.cpp @@ -18,7 +18,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mystdint.h" #include "common.h" #include "hqx.h" diff --git a/src/gl/hqnx/hqx.h b/src/gl/hqnx/hqx.h index ee968a561..089b5a0bc 100644 --- a/src/gl/hqnx/hqx.h +++ b/src/gl/hqnx/hqx.h @@ -21,7 +21,7 @@ #ifndef __HQX_H_ #define __HQX_H_ -#include "mystdint.h" +#include #if defined( __GNUC__ ) #ifdef __MINGW32__ diff --git a/src/gl/hqnx/init.cpp b/src/gl/hqnx/init.cpp index f27d45a65..0e8c2db1c 100644 --- a/src/gl/hqnx/init.cpp +++ b/src/gl/hqnx/init.cpp @@ -16,7 +16,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "mystdint.h" #include "hqx.h" uint32_t *RGBtoYUV; diff --git a/src/gl/hqnx/mystdint.h b/src/gl/hqnx/mystdint.h deleted file mode 100644 index b2da9df8c..000000000 --- a/src/gl/hqnx/mystdint.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __MYSTDINT_H -#define __MYSTDINT_H - -#ifndef _MSC_VER -#include -#else -typedef unsigned __int64 uint64_t; -typedef signed __int64 int64_t; -typedef unsigned __int32 uint32_t; -typedef signed __int32 int32_t; -typedef unsigned __int16 uint16_t; -typedef signed __int16 int16_t; -typedef unsigned __int8 uint8_t; -typedef signed __int8 int8_t; -#endif - - - -#endif \ No newline at end of file From 77dde2e3addbd7e44083b027059f102c0dc737c7 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sat, 3 Sep 2016 00:13:05 +0200 Subject: [PATCH 11/35] Fix not switching back to the default frame buffer when gl_renderbuffers is toggled off --- src/gl/renderer/gl_renderbuffers.cpp | 29 +++++++++++++--------------- src/gl/renderer/gl_renderbuffers.h | 3 +-- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index 35be00abc..5fe2f5b3a 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -156,6 +156,13 @@ void FGLRenderBuffers::DeleteFrameBuffer(GLuint &handle) bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHeight) { + if (gl_renderbuffers != BuffersActive) + { + if (BuffersActive) + glBindFramebuffer(GL_FRAMEBUFFER, mOutputFB); + BuffersActive = gl_renderbuffers; + } + if (!IsEnabled()) return false; @@ -223,7 +230,7 @@ void FGLRenderBuffers::CreateScene(int width, int height, int samples) ClearScene(); if (samples > 1) - mSceneMultisample = CreateRenderBuffer("SceneMultisample", GetHdrFormat(), samples, width, height); + mSceneMultisample = CreateRenderBuffer("SceneMultisample", GL_RGBA16F, samples, width, height); mSceneDepthStencil = CreateRenderBuffer("SceneDepthStencil", GL_DEPTH24_STENCIL8, samples, width, height); mSceneFB = CreateFrameBuffer("SceneFB", samples > 1 ? mSceneMultisample : mPipelineTexture[0], mSceneDepthStencil, samples > 1); @@ -241,7 +248,7 @@ void FGLRenderBuffers::CreatePipeline(int width, int height) for (int i = 0; i < NumPipelineTextures; i++) { - mPipelineTexture[i] = Create2DTexture("PipelineTexture", GetHdrFormat(), width, height); + mPipelineTexture[i] = Create2DTexture("PipelineTexture", GL_RGBA16F, width, height); mPipelineFB[i] = CreateFrameBuffer("PipelineFB", mPipelineTexture[i]); } } @@ -268,8 +275,8 @@ void FGLRenderBuffers::CreateBloom(int width, int height) level.Width = MAX(bloomWidth / 2, 1); level.Height = MAX(bloomHeight / 2, 1); - level.VTexture = Create2DTexture("Bloom.VTexture", GetHdrFormat(), level.Width, level.Height); - level.HTexture = Create2DTexture("Bloom.HTexture", GetHdrFormat(), level.Width, level.Height); + level.VTexture = Create2DTexture("Bloom.VTexture", GL_RGBA16F, level.Width, level.Height); + level.HTexture = Create2DTexture("Bloom.HTexture", GL_RGBA16F, level.Width, level.Height); level.VFramebuffer = CreateFrameBuffer("Bloom.VFramebuffer", level.VTexture); level.HFramebuffer = CreateFrameBuffer("Bloom.HFramebuffer", level.HTexture); @@ -278,17 +285,6 @@ void FGLRenderBuffers::CreateBloom(int width, int height) } } -//========================================================================== -// -// Fallback support for older OpenGL where RGBA16F might not be available -// -//========================================================================== - -GLuint FGLRenderBuffers::GetHdrFormat() -{ - return GL_RGBA16F; -} - //========================================================================== // // Creates a 2D texture defaulting to linear filtering and clamp to edge @@ -557,7 +553,8 @@ void FGLRenderBuffers::BindOutputFB() bool FGLRenderBuffers::IsEnabled() { - return gl_renderbuffers && !gl.legacyMode && !FailedCreate; + return BuffersActive && !gl.legacyMode && !FailedCreate; } bool FGLRenderBuffers::FailedCreate = false; +bool FGLRenderBuffers::BuffersActive = false; diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index ee6d8de5e..08303a912 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -59,8 +59,6 @@ private: void DeleteRenderBuffer(GLuint &handle); void DeleteFrameBuffer(GLuint &handle); - GLuint GetHdrFormat(); - int mWidth = 0; int mHeight = 0; int mSamples = 0; @@ -86,6 +84,7 @@ private: GLuint mOutputFB = 0; static bool FailedCreate; + static bool BuffersActive; }; #endif \ No newline at end of file From 90ab0223a6cf11e3e37b4e57c0fbef90d445eef7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 3 Sep 2016 00:36:23 +0200 Subject: [PATCH 12/35] - handle colormap parameter reset when renderbuffers are toggled. --- src/gl/renderer/gl_renderbuffers.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index 5fe2f5b3a..f66ea1354 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -55,14 +55,7 @@ #include "doomerrors.h" CVAR(Int, gl_multisample, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -CUSTOM_CVAR(Bool, gl_renderbuffers, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) -{ - // this CVAR alters some fixed colormap related settings - if (GLRenderer != nullptr && GLRenderer->mShaderManager != nullptr) - { - //GLRenderer->mShaderManager->ResetFixedColormap(); - } -} +CVAR(Bool, gl_renderbuffers, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) //========================================================================== // @@ -161,6 +154,7 @@ bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei if (BuffersActive) glBindFramebuffer(GL_FRAMEBUFFER, mOutputFB); BuffersActive = gl_renderbuffers; + GLRenderer->mShaderManager->ResetFixedColormap(); } if (!IsEnabled()) From 8b7a87f256e3ac1e42b429766dbcad6ac1e29627 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 3 Sep 2016 00:43:18 +0200 Subject: [PATCH 13/35] - fix conditions for DrawBlend calls. --- src/gl/scene/gl_scene.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index c1f022ab0..31aea734c 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -688,7 +688,7 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) gl_RenderState.SetFixedColormap(CM_DEFAULT); gl_RenderState.SetSoftLightLevel(-1); DrawTargeterSprites(); - if (FGLRenderBuffers::IsEnabled()) + if (!FGLRenderBuffers::IsEnabled()) { DrawBlend(viewsector); } @@ -970,7 +970,10 @@ void FGLRenderer::WriteSavePic (player_t *player, FILE *file, int width, int hei gl_RenderState.SetFixedColormap(CM_DEFAULT); gl_RenderState.SetSoftLightLevel(-1); screen->Begin2D(false); - DrawBlend(viewsector); + if (!FGLRenderBuffers::IsEnabled()) + { + DrawBlend(viewsector); + } CopyToBackbuffer(&bounds, false); glFlush(); From d14782fb37fc2283b2043e200a75c234fe298204 Mon Sep 17 00:00:00 2001 From: arookas Date: Wed, 31 Aug 2016 22:04:22 -0400 Subject: [PATCH 14/35] Added Thing_Damage3 function It acts as a simple wrapper around P_DamageMobj which can damage a single actor, but can also set the actor inflicting the damage. It returns the amount of damage actually done, or -1 if the damaging was cancelled. --- src/p_acs.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index a2b5275cb..edabca784 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4450,6 +4450,7 @@ enum EACSFunctions */ ACSF_CheckClass = 200, + ACSF_Thing_Damage3, // [arookas] // ZDaemon ACSF_GetTeamScore = 19620, // (int team) @@ -6035,6 +6036,15 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) const char *clsname = FBehavior::StaticLookupString(args[0]); return !!PClass::FindActor(clsname); } + + case ACSF_Thing_Damage3: // [arookas] wrapper around P_DamageMobj + { + // (target, ptr_select1, inflictor, ptr_select2, amount, damagetype) + AActor* target = COPY_AAPTR(SingleActorFromTID(args[0], activator), args[1]); + AActor* inflictor = COPY_AAPTR(SingleActorFromTID(args[2], activator), args[3]); + FName damagetype(FBehavior::StaticLookupString(args[5])); + return P_DamageMobj(target, inflictor, inflictor, args[4], damagetype); + } default: break; From d7b5bdc0f7e68b45fe2f76f8da35b61459e441dc Mon Sep 17 00:00:00 2001 From: arookas Date: Fri, 2 Sep 2016 23:04:12 -0400 Subject: [PATCH 15/35] Renamed Thing_Damage3 to DamageActor --- src/p_acs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index edabca784..0157fda75 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4450,7 +4450,7 @@ enum EACSFunctions */ ACSF_CheckClass = 200, - ACSF_Thing_Damage3, // [arookas] + ACSF_DamageActor, // [arookas] // ZDaemon ACSF_GetTeamScore = 19620, // (int team) @@ -6037,7 +6037,7 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return !!PClass::FindActor(clsname); } - case ACSF_Thing_Damage3: // [arookas] wrapper around P_DamageMobj + case ACSF_DamageActor: // [arookas] wrapper around P_DamageMobj { // (target, ptr_select1, inflictor, ptr_select2, amount, damagetype) AActor* target = COPY_AAPTR(SingleActorFromTID(args[0], activator), args[1]); From 5770e5dfaf0711619f3bb1f17fdd89065f98983d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 3 Sep 2016 12:00:08 +0200 Subject: [PATCH 16/35] - split up m_specialpaths.cpp to be a separate file for each operating system. The reason for this is that the macOS version uses a deprecated API and in order to correct this, the file needs to be compiled as Objective-C++ which requires a different extension. --- src/CMakeLists.txt | 14 +- src/posix/osx/i_specialpaths.mm | 187 +++++++++ src/posix/unix/i_specialpaths.cpp | 199 ++++++++++ .../i_specialpaths.cpp} | 360 ++---------------- 4 files changed, 429 insertions(+), 331 deletions(-) create mode 100644 src/posix/osx/i_specialpaths.mm create mode 100644 src/posix/unix/i_specialpaths.cpp rename src/{m_specialpaths.cpp => win32/i_specialpaths.cpp} (50%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f6caa2f5d..51548afdd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -635,6 +635,7 @@ set( PLAT_WIN32_SOURCES win32/i_main.cpp win32/i_movie.cpp win32/i_system.cpp + win32/i_specialpaths.cpp win32/st_start.cpp win32/win32video.cpp ) set( PLAT_POSIX_SOURCES @@ -652,8 +653,11 @@ set( PLAT_SDL_SOURCES posix/sdl/i_timer.cpp posix/sdl/sdlvideo.cpp posix/sdl/st_start.cpp ) +set( PLAT_UNIX_SOURCES + posix/unix/i_specialpaths.cpp ) set( PLAT_OSX_SOURCES posix/osx/iwadpicker_cocoa.mm + posix/osx/i_specialpaths.mm posix/osx/zdoom.icns ) set( PLAT_COCOA_SOURCES posix/cocoa/critsec.cpp @@ -670,7 +674,7 @@ set( PLAT_COCOA_SOURCES if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ${PLAT_UNIX_SOURCES} ) if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) # CMake is not set up to compile and link rc files with GCC. :( @@ -685,12 +689,12 @@ elseif( APPLE ) if( OSX_COCOA_BACKEND ) set( SYSTEM_SOURCES_DIR posix posix/cocoa ) set( SYSTEM_SOURCES ${PLAT_COCOA_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_UNIX_SOURCES} ) else() set( SYSTEM_SOURCES_DIR posix posix/sdl ) set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) set( PLAT_OSX_SOURCES ${PLAT_OSX_SOURCES} posix/sdl/i_system.mm ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ${PLAT_UNIX_SOURCES} ) endif() set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_OSX_SOURCES} "${FMOD_LIBRARY}" ) @@ -700,7 +704,7 @@ elseif( APPLE ) set_source_files_properties( posix/osx/iwadpicker_cocoa.mm PROPERTIES COMPILE_FLAGS -fobjc-exceptions ) else() set( SYSTEM_SOURCES_DIR posix posix/sdl ) - set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ) + set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_UNIX_SOURCES} ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) endif() @@ -1246,7 +1250,6 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE ${FASTMATH_SOURCES} ${PCH_SOURCES} x86.cpp - m_specialpaths.cpp strnatcmp.c zstring.cpp math/asin.c @@ -1410,6 +1413,7 @@ source_group("Resource Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r source_group("POSIX Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/.+") source_group("Cocoa Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/cocoa/.+") source_group("OS X Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/.+") +source_group("Unix Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/unix/.+") source_group("SDL Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/sdl/.+") source_group("SFMT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sfmt/.+") source_group("Shared Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_shared/.+") diff --git a/src/posix/osx/i_specialpaths.mm b/src/posix/osx/i_specialpaths.mm new file mode 100644 index 000000000..843fb82d6 --- /dev/null +++ b/src/posix/osx/i_specialpaths.mm @@ -0,0 +1,187 @@ +/* +** i_specialpaths.mm +** Gets special system folders where data should be stored. (macOS version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#include "cmdlib.h" +#include "m_misc.h" +#include "version.h" // for GAMENAME + +//=========================================================================== +// +// M_GetCachePath macOS +// +// Returns the path for cache GL nodes. +// +//=========================================================================== + +FString M_GetCachePath(bool create) +{ + FString path; + + char pathstr[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX)) + { + path = pathstr; + } + else + { + path = progdir; + } + path += "/zdoom/cache"; + return path; +} + +//=========================================================================== +// +// M_GetAutoexecPath macOS +// +// Returns the expected location of autoexec.cfg. +// +//=========================================================================== + +FString M_GetAutoexecPath() +{ + FString path; + + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/autoexec.cfg"; + } + return path; +} + +//=========================================================================== +// +// M_GetCajunPath macOS +// +// Returns the location of the Cajun Bot definitions. +// +//=========================================================================== + +FString M_GetCajunPath(const char *botfilename) +{ + FString path; + + // Just copies the Windows code. Should this be more Mac-specific? + path << progdir << "zcajun/" << botfilename; + if (!FileExists(path)) + { + path = ""; + } + return path; +} + +//=========================================================================== +// +// M_GetConfigPath macOS +// +// Returns the path to the config file. On Windows, this can vary for reading +// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try +// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. +// +//=========================================================================== + +FString M_GetConfigPath(bool for_reading) +{ + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kPreferencesFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + FString path; + path << cpath << "/" GAMENAMELOWERCASE ".ini"; + return path; + } + // Ungh. + return GAMENAMELOWERCASE ".ini"; +} + +//=========================================================================== +// +// M_GetScreenshotsPath macOS +// +// Returns the path to the default screenshots directory. +// +//=========================================================================== + +FString M_GetScreenshotsPath() +{ + FString path; + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/Screenshots/"; + } + else + { + path = "~/"; + } + return path; +} + +//=========================================================================== +// +// M_GetSavegamesPath macOS +// +// Returns the path to the default save games directory. +// +//=========================================================================== + +FString M_GetSavegamesPath() +{ + FString path; + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/Savegames/"; + } + return path; +} + diff --git a/src/posix/unix/i_specialpaths.cpp b/src/posix/unix/i_specialpaths.cpp new file mode 100644 index 000000000..6a17e1318 --- /dev/null +++ b/src/posix/unix/i_specialpaths.cpp @@ -0,0 +1,199 @@ +/* +** i_specialpaths.cpp +** Gets special system folders where data should be stored. (Unix version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include "i_system.h" + +#include "version.h" // for GAMENAME + + +FString GetUserFile (const char *file) +{ + FString path; + struct stat info; + + path = NicePath("~/" GAME_DIR "/"); + + if (stat (path, &info) == -1) + { + struct stat extrainfo; + + // Sanity check for ~/.config + FString configPath = NicePath("~/.config/"); + if (stat (configPath, &extrainfo) == -1) + { + if (mkdir (configPath, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + I_FatalError ("Failed to create ~/.config directory:\n%s", strerror(errno)); + } + } + else if (!S_ISDIR(extrainfo.st_mode)) + { + I_FatalError ("~/.config must be a directory"); + } + + // This can be removed after a release or two + // Transfer the old zdoom directory to the new location + bool moved = false; + FString oldpath = NicePath("~/." GAMENAMELOWERCASE "/"); + if (stat (oldpath, &extrainfo) != -1) + { + if (rename(oldpath, path) == -1) + { + I_Error ("Failed to move old " GAMENAMELOWERCASE " directory (%s) to new location (%s).", + oldpath.GetChars(), path.GetChars()); + } + else + moved = true; + } + + if (!moved && mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + I_FatalError ("Failed to create %s directory:\n%s", + path.GetChars(), strerror (errno)); + } + } + else + { + if (!S_ISDIR(info.st_mode)) + { + I_FatalError ("%s must be a directory", path.GetChars()); + } + } + path += file; + return path; +} + +//=========================================================================== +// +// M_GetCachePath Unix +// +// Returns the path for cache GL nodes. +// +//=========================================================================== + +FString M_GetCachePath(bool create) +{ + // Don't use GAME_DIR and such so that ZDoom and its child ports can + // share the node cache. + FString path = NicePath("~/.config/zdoom/cache"); + if (create) + { + CreatePath(path); + } + return path; +} + +//=========================================================================== +// +// M_GetAutoexecPath Unix +// +// Returns the expected location of autoexec.cfg. +// +//=========================================================================== + +FString M_GetAutoexecPath() +{ + return GetUserFile("autoexec.cfg"); +} + +//=========================================================================== +// +// M_GetCajunPath Unix +// +// Returns the location of the Cajun Bot definitions. +// +//=========================================================================== + +FString M_GetCajunPath(const char *botfilename) +{ + FString path; + + // Check first in ~/.config/zdoom/botfilename. + path = GetUserFile(botfilename); + if (!FileExists(path)) + { + // Then check in SHARE_DIR/botfilename. + path = SHARE_DIR; + path << botfilename; + if (!FileExists(path)) + { + path = ""; + } + } + return path; +} + +//=========================================================================== +// +// M_GetConfigPath Unix +// +// Returns the path to the config file. On Windows, this can vary for reading +// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try +// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. +// +//=========================================================================== + +FString M_GetConfigPath(bool for_reading) +{ + return GetUserFile(GAMENAMELOWERCASE ".ini"); +} + +//=========================================================================== +// +// M_GetScreenshotsPath Unix +// +// Returns the path to the default screenshots directory. +// +//=========================================================================== + +FString M_GetScreenshotsPath() +{ + return NicePath("~/" GAME_DIR "/screenshots/"); +} + +//=========================================================================== +// +// M_GetSavegamesPath Unix +// +// Returns the path to the default save games directory. +// +//=========================================================================== + +FString M_GetSavegamesPath() +{ + return NicePath("~/" GAME_DIR); +} diff --git a/src/m_specialpaths.cpp b/src/win32/i_specialpaths.cpp similarity index 50% rename from src/m_specialpaths.cpp rename to src/win32/i_specialpaths.cpp index abfb5db8f..ed8dc2ee6 100644 --- a/src/m_specialpaths.cpp +++ b/src/win32/i_specialpaths.cpp @@ -1,27 +1,46 @@ -#ifdef __APPLE__ -#include -#endif +/* +** i_specialpaths.cpp +** Gets special system folders where data should be stored. (Windows version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ -#ifdef _WIN32 #include #include #include #define USE_WINDOWS_DWORD -#endif #include "cmdlib.h" #include "m_misc.h" - -#if !defined(__APPLE__) && !defined(_WIN32) -#include -#include -#include "i_system.h" -#endif - #include "version.h" // for GAMENAME - -#if defined(_WIN32) - #include "i_system.h" typedef HRESULT (WINAPI *GKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *); @@ -323,314 +342,3 @@ FString M_GetSavegamesPath() } return path; } - -#elif defined(__APPLE__) - -//=========================================================================== -// -// M_GetCachePath Mac OS X -// -// Returns the path for cache GL nodes. -// -//=========================================================================== - -FString M_GetCachePath(bool create) -{ - FString path; - - char pathstr[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX)) - { - path = pathstr; - } - else - { - path = progdir; - } - path += "/zdoom/cache"; - return path; -} - -//=========================================================================== -// -// M_GetAutoexecPath Mac OS X -// -// Returns the expected location of autoexec.cfg. -// -//=========================================================================== - -FString M_GetAutoexecPath() -{ - FString path; - - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/autoexec.cfg"; - } - return path; -} - -//=========================================================================== -// -// M_GetCajunPath Mac OS X -// -// Returns the location of the Cajun Bot definitions. -// -//=========================================================================== - -FString M_GetCajunPath(const char *botfilename) -{ - FString path; - - // Just copies the Windows code. Should this be more Mac-specific? - path << progdir << "zcajun/" << botfilename; - if (!FileExists(path)) - { - path = ""; - } - return path; -} - -//=========================================================================== -// -// M_GetConfigPath Mac OS X -// -// Returns the path to the config file. On Windows, this can vary for reading -// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try -// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. -// -//=========================================================================== - -FString M_GetConfigPath(bool for_reading) -{ - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kPreferencesFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - FString path; - path << cpath << "/" GAMENAMELOWERCASE ".ini"; - return path; - } - // Ungh. - return GAMENAMELOWERCASE ".ini"; -} - -//=========================================================================== -// -// M_GetScreenshotsPath Mac OS X -// -// Returns the path to the default screenshots directory. -// -//=========================================================================== - -FString M_GetScreenshotsPath() -{ - FString path; - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/Screenshots/"; - } - else - { - path = "~/"; - } - return path; -} - -//=========================================================================== -// -// M_GetSavegamesPath Mac OS X -// -// Returns the path to the default save games directory. -// -//=========================================================================== - -FString M_GetSavegamesPath() -{ - FString path; - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/Savegames/"; - } - return path; -} - -#else // Linux, et al. - - -FString GetUserFile (const char *file) -{ - FString path; - struct stat info; - - path = NicePath("~/" GAME_DIR "/"); - - if (stat (path, &info) == -1) - { - struct stat extrainfo; - - // Sanity check for ~/.config - FString configPath = NicePath("~/.config/"); - if (stat (configPath, &extrainfo) == -1) - { - if (mkdir (configPath, S_IRUSR | S_IWUSR | S_IXUSR) == -1) - { - I_FatalError ("Failed to create ~/.config directory:\n%s", strerror(errno)); - } - } - else if (!S_ISDIR(extrainfo.st_mode)) - { - I_FatalError ("~/.config must be a directory"); - } - - // This can be removed after a release or two - // Transfer the old zdoom directory to the new location - bool moved = false; - FString oldpath = NicePath("~/." GAMENAMELOWERCASE "/"); - if (stat (oldpath, &extrainfo) != -1) - { - if (rename(oldpath, path) == -1) - { - I_Error ("Failed to move old " GAMENAMELOWERCASE " directory (%s) to new location (%s).", - oldpath.GetChars(), path.GetChars()); - } - else - moved = true; - } - - if (!moved && mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) - { - I_FatalError ("Failed to create %s directory:\n%s", - path.GetChars(), strerror (errno)); - } - } - else - { - if (!S_ISDIR(info.st_mode)) - { - I_FatalError ("%s must be a directory", path.GetChars()); - } - } - path += file; - return path; -} - -//=========================================================================== -// -// M_GetCachePath Unix -// -// Returns the path for cache GL nodes. -// -//=========================================================================== - -FString M_GetCachePath(bool create) -{ - // Don't use GAME_DIR and such so that ZDoom and its child ports can - // share the node cache. - FString path = NicePath("~/.config/zdoom/cache"); - if (create) - { - CreatePath(path); - } - return path; -} - -//=========================================================================== -// -// M_GetAutoexecPath Unix -// -// Returns the expected location of autoexec.cfg. -// -//=========================================================================== - -FString M_GetAutoexecPath() -{ - return GetUserFile("autoexec.cfg"); -} - -//=========================================================================== -// -// M_GetCajunPath Unix -// -// Returns the location of the Cajun Bot definitions. -// -//=========================================================================== - -FString M_GetCajunPath(const char *botfilename) -{ - FString path; - - // Check first in ~/.config/zdoom/botfilename. - path = GetUserFile(botfilename); - if (!FileExists(path)) - { - // Then check in SHARE_DIR/botfilename. - path = SHARE_DIR; - path << botfilename; - if (!FileExists(path)) - { - path = ""; - } - } - return path; -} - -//=========================================================================== -// -// M_GetConfigPath Unix -// -// Returns the path to the config file. On Windows, this can vary for reading -// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try -// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. -// -//=========================================================================== - -FString M_GetConfigPath(bool for_reading) -{ - return GetUserFile(GAMENAMELOWERCASE ".ini"); -} - -//=========================================================================== -// -// M_GetScreenshotsPath Unix -// -// Returns the path to the default screenshots directory. -// -//=========================================================================== - -FString M_GetScreenshotsPath() -{ - return NicePath("~/" GAME_DIR "/screenshots/"); -} - -//=========================================================================== -// -// M_GetSavegamesPath Unix -// -// Returns the path to the default save games directory. -// -//=========================================================================== - -FString M_GetSavegamesPath() -{ - return NicePath("~/" GAME_DIR); -} - -#endif From aece9aaa58bed5864fde2e7a84976e0afaf5288f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 3 Sep 2016 14:01:51 +0200 Subject: [PATCH 17/35] - added xBRZ texture scaler after clearing the licensing conditions. A screenshot of the confirmation that this is ok has been added to the 'licenses' folder. --- docs/licenses/xBRZ.jpg | Bin 0 -> 60099 bytes src/CMakeLists.txt | 6 +++- src/gl/data/gl_vertexbuffer.h | 10 ++++++ src/gl/scene/gl_scene.cpp | 2 -- src/gl/shaders/gl_shader.h | 3 +- src/gl/textures/gl_hqresize.cpp | 62 +++++++++++++++++++++++++++++--- wadsrc/static/menudef.z | 6 ++++ 7 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 docs/licenses/xBRZ.jpg diff --git a/docs/licenses/xBRZ.jpg b/docs/licenses/xBRZ.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ab8335391b0e35541662e9664ad53c31aaed0c51 GIT binary patch literal 60099 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 VZ-P$?YO$&>=SYI 9) -#else - if (self < 0 || self > 6) -#endif + if (self < 0 || self > 16) + { self = 0; + } + #ifndef HAVE_MMX + // This is to allow the menu option to work properly so that these filters can be skipped while cycling through them. + if (self == 7) self = 10; + if (self == 8) self = 10; + if (self == 9) self = 6; + #endif GLRenderer->FlushTextures(); } @@ -242,6 +248,42 @@ static unsigned char *hqNxHelper( void (*hqNxFunction) ( unsigned*, unsigned*, i } + +static unsigned char *xbrzHelper( void (*xbrzFunction) ( size_t, const uint32_t*, uint32_t*, int, int, xbrz::ColorFormat, const xbrz::ScalerCfg&, int, int ), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + outWidth = N * inWidth; + outHeight = N *inHeight; + + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + xbrzFunction(N, reinterpret_cast(inputBuffer), reinterpret_cast(newBuffer), inWidth, inHeight, xbrz::ARGB, xbrz::ScalerCfg(), 0, std::numeric_limits::max()); + delete[] inputBuffer; + return newBuffer; +} + +static unsigned char *xbrzoldHelper( void (*xbrzFunction) ( size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz_old::ScalerCfg& cfg, int yFirst, int yLast ), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + outWidth = N * inWidth; + outHeight = N *inHeight; + + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + xbrzFunction(N, reinterpret_cast(inputBuffer), reinterpret_cast(newBuffer), inWidth, inHeight, xbrz_old::ScalerCfg(), 0, std::numeric_limits::max()); + delete[] inputBuffer; + return newBuffer; +} + + //=========================================================================== // // [BB] Upsamples the texture in inputBuffer, frees inputBuffer and returns @@ -322,6 +364,16 @@ unsigned char *gl_CreateUpsampledTextureBuffer ( const FTexture *inputTexture, u case 9: return hqNxAsmHelper( &HQnX_asm::hq4x_32, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); #endif + case 10: + case 11: + case 12: + return xbrzHelper(xbrz::scale, type - 8, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + + case 13: + case 14: + case 15: + return xbrzoldHelper(xbrz_old::scale, type - 11, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + } } return inputBuffer; diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index 2386b1076..86e2d5dc0 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -134,6 +134,12 @@ OptionValue "HqResizeModes" 7, "$OPTVAL_HQ2XMMX" 8, "$OPTVAL_HQ3XMMX" 9, "$OPTVAL_HQ4XMMX" + 10, "xBRZ 2x" + 11, "xBRZ 3x" + 12, "xBRZ 4x" + 13, "xBRZ_old 2x" + 14, "xBRZ_old 3x" + 15, "xBRZ_old 4x" } OptionValue "FogMode" From 4a80f8e4ed0bfff6e56bfef8bc1f302599253958 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 3 Sep 2016 16:54:17 +0200 Subject: [PATCH 18/35] - fixed: Camera textures and savegame pictures should not be drawn with a Stereo3D mode. --- src/gl/scene/gl_scene.cpp | 4 ++-- src/gl/stereo3d/gl_stereo3d.h | 1 + src/gl/stereo3d/gl_stereo_cvars.cpp | 7 +++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 82b63aa00..b4dd95d50 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -822,7 +822,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo // Render (potentially) multiple views for stereo 3d float viewShift[3]; - const s3d::Stereo3DMode& stereo3dMode = s3d::Stereo3DMode::getCurrentMode(); + const s3d::Stereo3DMode& stereo3dMode = mainview && toscreen? s3d::Stereo3DMode::getCurrentMode() : s3d::Stereo3DMode::getMonoMode(); stereo3dMode.SetUp(); for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix) { @@ -1313,7 +1313,7 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in gl_fixedcolormap=CM_DEFAULT; gl_RenderState.SetFixedColormap(CM_DEFAULT); - bool usefb = gl_usefb || gltex->GetWidth() > screen->GetWidth() || gltex->GetHeight() > screen->GetHeight(); + bool usefb = !gl.legacyMode || gl_usefb || gltex->GetWidth() > screen->GetWidth() || gltex->GetHeight() > screen->GetHeight(); if (!usefb) { glFlush(); diff --git a/src/gl/stereo3d/gl_stereo3d.h b/src/gl/stereo3d/gl_stereo3d.h index 303f18825..c56cc078f 100644 --- a/src/gl/stereo3d/gl_stereo3d.h +++ b/src/gl/stereo3d/gl_stereo3d.h @@ -74,6 +74,7 @@ class Stereo3DMode public: /* static methods for managing the selected stereoscopic view state */ static const Stereo3DMode& getCurrentMode(); + static const Stereo3DMode& getMonoMode(); Stereo3DMode(); virtual ~Stereo3DMode(); diff --git a/src/gl/stereo3d/gl_stereo_cvars.cpp b/src/gl/stereo3d/gl_stereo_cvars.cpp index e7d08df41..897b28088 100644 --- a/src/gl/stereo3d/gl_stereo_cvars.cpp +++ b/src/gl/stereo3d/gl_stereo_cvars.cpp @@ -105,5 +105,12 @@ const Stereo3DMode& Stereo3DMode::getCurrentMode() return *currentStereo3DMode; } +const Stereo3DMode& Stereo3DMode::getMonoMode() +{ + setCurrentMode(MonoView::getInstance()); + return *currentStereo3DMode; +} + + } /* namespace s3d */ From 3ae2e77512acdc65b89d958345fa2fb73633d794 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 3 Sep 2016 16:55:16 +0200 Subject: [PATCH 19/35] - added xBRZ files. --- src/gl/xbr/xbrz.cpp | 1229 ++++++++++++++++++++++++++++++ src/gl/xbr/xbrz.h | 102 +++ src/gl/xbr/xbrz_config.h | 45 ++ src/gl/xbr/xbrz_config_old.h | 45 ++ src/gl/xbr/xbrz_old.cpp | 1365 ++++++++++++++++++++++++++++++++++ src/gl/xbr/xbrz_old.h | 92 +++ 6 files changed, 2878 insertions(+) create mode 100644 src/gl/xbr/xbrz.cpp create mode 100644 src/gl/xbr/xbrz.h create mode 100644 src/gl/xbr/xbrz_config.h create mode 100644 src/gl/xbr/xbrz_config_old.h create mode 100644 src/gl/xbr/xbrz_old.cpp create mode 100644 src/gl/xbr/xbrz_old.h diff --git a/src/gl/xbr/xbrz.cpp b/src/gl/xbr/xbrz.cpp new file mode 100644 index 000000000..b26d4bbd3 --- /dev/null +++ b/src/gl/xbr/xbrz.cpp @@ -0,0 +1,1229 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#include "xbrz.h" + +#include +#include +#include +#include + +#if __cplusplus <= 199711 +#define static_assert(VAL, MSG) static_assertion(); +template struct static_assertion; +template<> struct static_assertion {}; +#endif // __cplusplus <= 199711 + +namespace +{ +template inline +unsigned char getByte(uint32_t val) { return static_cast((val >> (8 * N)) & 0xff); } + +inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); } +inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); } +inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); } +inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); } + +inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; } +inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; } + + +template inline +uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending +{ + static_assert(0 < M && M < N && N <= 1000, ""); + +#define calcColor(colFront, colBack) \ + (((colFront) * M + (colBack) * (N - M)) / N) + + return makePixel(calcColor(getRed (pixFront), getRed (pixBack)), + calcColor(getGreen(pixFront), getGreen(pixBack)), + calcColor(getBlue (pixFront), getBlue (pixBack))); + +#undef calcColor +} + + +template inline +uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!) +{ + static_assert(0 < M && M < N && N <= 1000, ""); + + const unsigned int weightFront = getAlpha(pixFront) * M; + const unsigned int weightBack = getAlpha(pixBack) * (N - M); + const unsigned int weightSum = weightFront + weightBack; + if (weightSum == 0) + return 0; + +#define calcColor(colFront, colBack) \ + static_cast(((colFront) * weightFront + (colBack) * weightBack) / weightSum) + + return makePixel(static_cast(weightSum / N), + calcColor(getRed (pixFront), getRed (pixBack)), + calcColor(getGreen(pixFront), getGreen(pixBack)), + calcColor(getBlue (pixFront), getBlue (pixBack))); + +#undef calcColor +} + + +//inline +//double fastSqrt(double n) +//{ +// __asm //speeds up xBRZ by about 9% compared to std::sqrt which internally uses the same assembler instructions but adds some "fluff" +// { +// fld n +// fsqrt +// } +//} +// + + +uint32_t* byteAdvance( uint32_t* ptr, int bytes) { return reinterpret_cast< uint32_t*>(reinterpret_cast< char*>(ptr) + bytes); } +const uint32_t* byteAdvance(const uint32_t* ptr, int bytes) { return reinterpret_cast(reinterpret_cast(ptr) + bytes); } + + +//fill block with the given color +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int blockWidth, int blockHeight) +{ + //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + // std::fill(trg, trg + blockWidth, col); + + for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + for (int x = 0; x < blockWidth; ++x) + trg[x] = col; +} + +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int n) { fillBlock(trg, pitch, col, n, n); } + + +#ifdef _MSC_VER + #define FORCE_INLINE __forceinline +#elif defined __GNUC__ + #define FORCE_INLINE __attribute__((always_inline)) inline +#else + #define FORCE_INLINE inline +#endif + + +enum RotationDegree //clock-wise +{ + ROT_0, + ROT_90, + ROT_180, + ROT_270 +}; + +//calculate input matrix coordinates after rotation at compile time +template +struct MatrixRotation; + +template +struct MatrixRotation +{ + static const size_t I_old = I; + static const size_t J_old = J; +}; + +template //(i, j) = (row, col) indices, N = size of (square) matrix +struct MatrixRotation +{ + static const size_t I_old = N - 1 - MatrixRotation(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation! + static const size_t J_old = MatrixRotation(rotDeg - 1), I, J, N>::I_old; // +}; + + +template +class OutputMatrix +{ +public: + OutputMatrix(uint32_t* out, int outWidth) : //access matrix area, top-left at position "out" for image with given width + out_(out), + outWidth_(outWidth) {} + + template + uint32_t& ref() const + { + static const size_t I_old = MatrixRotation::I_old; + static const size_t J_old = MatrixRotation::J_old; + return *(out_ + J_old + I_old * outWidth_); + } + +private: + uint32_t* out_; + const int outWidth_; +}; + + +template inline +T square(T value) { return value * value; } + + + +inline +double distRGB(uint32_t pix1, uint32_t pix2) +{ + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //euklidean RGB distance + return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff)); +} + + +inline +double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight) +{ + //http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion + //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first! + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); // + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double! + + //const double k_b = 0.0722; //ITU-R BT.709 conversion + //const double k_r = 0.2126; // + const double k_b = 0.0593; //ITU-R BT.2020 conversion + const double k_r = 0.2627; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + //we skip division by 255 to have similar range like other distance functions + return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r)); +} + + +struct DistYCbCrBuffer //30% perf boost compared to distYCbCr()! +{ +public: + static double dist(uint32_t pix1, uint32_t pix2) + { +#if defined _MSC_VER && _MSC_VER < 1900 +#error function scope static initialization is not yet thread-safe! +#endif + static const DistYCbCrBuffer inst; + return inst.distImpl(pix1, pix2); + } + +private: + DistYCbCrBuffer() : buffer(256 * 256 * 256) + { + for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores) + { + const int r_diff = getByte<2>(i) * 2 - 255; + const int g_diff = getByte<1>(i) * 2 - 255; + const int b_diff = getByte<0>(i) * 2 - 255; + + const double k_b = 0.0593; //ITU-R BT.2020 conversion + const double k_r = 0.2627; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + buffer[i] = static_cast(std::sqrt(square(y) + square(c_b) + square(c_r))); + } + } + + double distImpl(uint32_t pix1, uint32_t pix2) const + { + //if (pix1 == pix2) -> 8% perf degradation! + // return 0; + //if (pix1 > pix2) + // std::swap(pix1, pix2); -> 30% perf degradation!!! + + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + return buffer[(((r_diff + 255) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte + (((g_diff + 255) / 2) << 8) | + (( b_diff + 255) / 2)]; + } + + std::vector buffer; //consumes 64 MB memory; using double is only 2% faster, but takes 128 MB +}; + + +enum BlendType +{ + BLEND_NONE = 0, + BLEND_NORMAL, //a normal indication to blend + BLEND_DOMINANT, //a strong indication to blend + //attention: BlendType must fit into the value range of 2 bit!!! +}; + +struct BlendResult +{ + BlendType + /**/blend_f, blend_g, + /**/blend_j, blend_k; +}; + + +struct Kernel_4x4 //kernel for preprocessing step +{ + uint32_t + /**/a, b, c, d, + /**/e, f, g, h, + /**/i, j, k, l, + /**/m, n, o, p; +}; + +/* +input kernel area naming convention: +----------------- +| A | B | C | D | +----|---|---|---| +| E | F | G | H | //evaluate the four corners between F, G, J, K +----|---|---|---| //input pixel is at position F +| I | J | K | L | +----|---|---|---| +| M | N | O | P | +----------------- +*/ +template +FORCE_INLINE //detect blend direction +BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType" +{ + BlendResult result = {}; + + if ((ker.f == ker.g && + ker.j == ker.k) || + (ker.f == ker.j && + ker.g == ker.k)) + return result; + +#define dist(pix1, pix2) \ + ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) + + const int weight = 4; + double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g); + double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k); + +#undef dist + + if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8 + { + const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk; + if (ker.f != ker.g && ker.f != ker.j) + result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.k != ker.j && ker.k != ker.g) + result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + else if (fk < jg) + { + const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg; + if (ker.j != ker.f && ker.j != ker.k) + result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.g != ker.f && ker.g != ker.k) + result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + return result; +} + +struct Kernel_3x3 +{ + uint32_t + /**/a, b, c, + /**/d, e, f, + /**/g, h, i; +}; + +#define DEF_GETTER(x) template uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; } +//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token +DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c) +DEF_GETTER(d) DEF_GETTER(e) DEF_GETTER(f) +DEF_GETTER(g) DEF_GETTER(h) DEF_GETTER(i) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, g) DEF_GETTER(b, d) DEF_GETTER(c, a) +DEF_GETTER(d, h) DEF_GETTER(e, e) DEF_GETTER(f, b) +DEF_GETTER(g, i) DEF_GETTER(h, f) DEF_GETTER(i, c) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, i) DEF_GETTER(b, h) DEF_GETTER(c, g) +DEF_GETTER(d, f) DEF_GETTER(e, e) DEF_GETTER(f, d) +DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i) +DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h) +DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g) +#undef DEF_GETTER + + +//compress four blend types into a single byte +inline BlendType getTopL (unsigned char b) { return static_cast(0x3 & b); } +inline BlendType getTopR (unsigned char b) { return static_cast(0x3 & (b >> 2)); } +inline BlendType getBottomR(unsigned char b) { return static_cast(0x3 & (b >> 4)); } +inline BlendType getBottomL(unsigned char b) { return static_cast(0x3 & (b >> 6)); } + +inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing! +inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } +inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); } +inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); } + +inline bool blendingNeeded(unsigned char b) { return b != 0; } + +template inline +unsigned char rotateBlendInfo(unsigned char b) { return b; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 2) | (b >> 6)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 4) | (b >> 4)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; } + + +/* +input kernel area naming convention: +------------- +| A | B | C | +----|---|---| +| D | E | F | //input pixel is at position E +----|---|---| +| G | H | I | +------------- +*/ +template +FORCE_INLINE //perf: quite worth it! +void blendPixel(const Kernel_3x3& ker, + uint32_t* target, int trgWidth, + unsigned char blendInfo, //result of preprocessing all four corners of pixel "e" + const xbrz::ScalerCfg& cfg) +{ +#define a get_a(ker) +#define b get_b(ker) +#define c get_c(ker) +#define d get_d(ker) +#define e get_e(ker) +#define f get_f(ker) +#define g get_g(ker) +#define h get_h(ker) +#define i get_i(ker) + + const unsigned char blend = rotateBlendInfo(blendInfo); + + if (getBottomR(blend) >= BLEND_NORMAL) + { + struct LineBlend + { + static bool Eval(const Kernel_3x3& ker, const xbrz::ScalerCfg& cfg, const unsigned char blend) + { + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + +#define eq(pix1, pix2) \ + (ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) < cfg.equalColorTolerance) + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90 degree corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (!eq(e, i) && eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c)) + return false; + +#undef eq + + return true; + } + }; + + const bool doLineBlend = LineBlend::Eval(ker, cfg, blend); + +#define dist(pix1, pix2) \ + ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) + + const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color + + OutputMatrix out(target, trgWidth); + + if (doLineBlend) + { + const double fg = dist(f, g); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9 + const double hc = dist(h, c); // + + const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g; + const bool haveSteepLine = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c; + + if (haveShallowLine) + { + if (haveSteepLine) + Scaler::blendLineSteepAndShallow(px, out); + else + Scaler::blendLineShallow(px, out); + } + else + { + if (haveSteepLine) + Scaler::blendLineSteep(px, out); + else + Scaler::blendLineDiagonal(px,out); + } + } + else + Scaler::blendCorner(px, out); + } + +#undef dist + +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h +#undef i +} + + +template //scaler policy: see "Scaler2x" reference implementation +void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) +{ + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || srcWidth <= 0) + return; + + const int trgWidth = srcWidth * Scaler::scale; + + //"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of + //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing + const int bufferSize = srcWidth; + unsigned char* preProcBuffer = reinterpret_cast(trg + yLast * Scaler::scale * trgWidth) - bufferSize; + std::fill(preProcBuffer, preProcBuffer + bufferSize, 0); + static_assert(BLEND_NONE == 0, ""); + + //initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending + //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition! + if (yFirst > 0) + { + const int y = yFirst - 1; + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + for (int x = 0; x < srcWidth; ++x) + { + const int x_m1 = std::max(x - 1, 0); + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker = {}; //perf: initialization is negligible + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //input pixel is at position F + | J | K | + --------- + */ + setTopR(preProcBuffer[x], res.blend_j); + + if (x + 1 < bufferSize) + setTopL(preProcBuffer[x + 1], res.blend_k); + } + } + //------------------------------------------------------------------------------------ + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position + + for (int x = 0; x < srcWidth; ++x, out += Scaler::scale) + { + //all those bounds checks have only insignificant impact on performance! + const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers! + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker4 = {}; //perf: initialization is negligible + + ker4.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker4.b = s_m1[x]; + ker4.c = s_m1[x_p1]; + ker4.d = s_m1[x_p2]; + + ker4.e = s_0[x_m1]; + ker4.f = s_0[x]; + ker4.g = s_0[x_p1]; + ker4.h = s_0[x_p2]; + + ker4.i = s_p1[x_m1]; + ker4.j = s_p1[x]; + ker4.k = s_p1[x_p1]; + ker4.l = s_p1[x_p2]; + + ker4.m = s_p2[x_m1]; + ker4.n = s_p2[x]; + ker4.o = s_p2[x_p1]; + ker4.p = s_p2[x_p2]; + + //evaluate the four corners on bottom-right of current pixel + unsigned char blend_xy = 0; //for current (x, y) position + { + const BlendResult res = preProcessCorners(ker4, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //current input pixel is at position F + | J | K | + --------- + */ + blend_xy = preProcBuffer[x]; + setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence! + + setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1) + preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row + + blend_xy1 = 0; + setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column + + if (x + 1 < bufferSize) //set 3rd known corner for (x + 1, y) + setBottomL(preProcBuffer[x + 1], res.blend_g); + } + + //fill block of size scale * scale with the given color + fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale); //place *after* preprocessing step, to not overwrite the results while processing the the last pixel! + + //blend four corners of current pixel + if (blendingNeeded(blend_xy)) //good 5% perf-improvement + { + Kernel_3x3 ker3 = {}; //perf: initialization is negligible + + ker3.a = ker4.a; + ker3.b = ker4.b; + ker3.c = ker4.c; + + ker3.d = ker4.e; + ker3.e = ker4.f; + ker3.f = ker4.g; + + ker3.g = ker4.i; + ker3.h = ker4.j; + ker3.i = ker4.k; + + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + } + } + } +} + +//------------------------------------------------------------------------------------ + +template +struct Scaler2x : public ColorGradient +{ + static const int scale = 2; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<1, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 1>(), col); + alphaGrad<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref<1, 1>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366 + } +}; + + +template +struct Scaler3x : public ColorGradient +{ + static const int scale = 3; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + out.template ref<2, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<2, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 2>(), col); + alphaGrad<3, 4>(out.template ref<2, 1>(), col); + alphaGrad<3, 4>(out.template ref<1, 2>(), col); + out.template ref<2, 2>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 8>(out.template ref<1, 2>(), col); //conflict with other rotations for this odd scale + alphaGrad<1, 8>(out.template ref<2, 1>(), col); + alphaGrad<7, 8>(out.template ref<2, 2>(), col); // + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598 + //alphaGrad<7, 256>(out.template ref<2, 1>(), col); //0.02826017254 -> negligible + avoid conflicts with other rotations for this odd scale + //alphaGrad<7, 256>(out.template ref<1, 2>(), col); //0.02826017254 + } +}; + + +template +struct Scaler4x : public ColorGradient +{ + static const int scale = 4; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<3, 4>(out.template ref<3, 1>(), col); + alphaGrad<3, 4>(out.template ref<1, 3>(), col); + alphaGrad<1, 4>(out.template ref<3, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 3>(), col); + + alphaGrad<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR + + out.template ref<3, 3>() = col; + out.template ref<3, 2>() = col; + out.template ref<2, 3>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563 + alphaGrad< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501 + alphaGrad< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501 + } +}; + + +template +struct Scaler5x : public ColorGradient +{ + static const int scale = 5; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<4, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + alphaGrad<2, 3>(out.template ref<3, 3>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 8>(out.template ref(), col); //conflict with other rotations for this odd scale + alphaGrad<1, 8>(out.template ref(), col); + alphaGrad<1, 8>(out.template ref(), col); // + + alphaGrad<7, 8>(out.template ref<4, 3>(), col); + alphaGrad<7, 8>(out.template ref<3, 4>(), col); + + out.template ref<4, 4>() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088 + alphaGrad<23, 100>(out.template ref<4, 3>(), col); //0.2306749731 + alphaGrad<23, 100>(out.template ref<3, 4>(), col); //0.2306749731 + //alphaGrad<1, 64>(out.template ref<4, 2>(), col); //0.01676812367 -> negligible + avoid conflicts with other rotations for this odd scale + //alphaGrad<1, 64>(out.template ref<2, 4>(), col); //0.01676812367 + } +}; + + +template +struct Scaler6x : public ColorGradient +{ + static const int scale = 6; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<5, scale - 3>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<97, 100>(out.template ref<5, 5>(), col); //exact: 0.9711013910 + alphaGrad<42, 100>(out.template ref<4, 5>(), col); //0.4236372243 + alphaGrad<42, 100>(out.template ref<5, 4>(), col); //0.4236372243 + alphaGrad< 6, 100>(out.template ref<5, 3>(), col); //0.05652034508 + alphaGrad< 6, 100>(out.template ref<3, 5>(), col); //0.05652034508 + } +}; + +//------------------------------------------------------------------------------------ + +struct ColorDistanceRGB +{ + static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) + { + return DistYCbCrBuffer::dist(pix1, pix2); + + //if (pix1 == pix2) //about 4% perf boost + // return 0; + //return distYCbCr(pix1, pix2, luminanceWeight); + } +}; + +struct ColorDistanceARGB +{ + static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) + { + const double a1 = getAlpha(pix1) / 255.0 ; + const double a2 = getAlpha(pix2) / 255.0 ; + /* + Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1] + + 1. if a1 = a2, distance should be: a1 * distYCbCr() + 2. if a1 = 0, distance should be: a2 * distYCbCr(black, white) = a2 * 255 + 3. if a1 = 1, ??? maybe: 255 * (1 - a2) + a2 * distYCbCr() + */ + + //return std::min(a1, a2) * DistYCbCrBuffer::dist(pix1, pix2) + 255 * abs(a1 - a2); + //=> following code is 15% faster: + const double d = DistYCbCrBuffer::dist(pix1, pix2); + if (a1 < a2) + return a1 * d + 255 * (a2 - a1); + else + return a2 * d + 255 * (a1 - a2); + + //alternative? return std::sqrt(a1 * a2 * square(DistYCbCrBuffer::dist(pix1, pix2)) + square(255 * (a1 - a2))); + } +}; + + +struct ColorGradientRGB +{ + template + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) + { + pixBack = gradientRGB(pixFront, pixBack); + } +}; + +struct ColorGradientARGB +{ + template + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) + { + pixBack = gradientARGB(pixFront, pixBack); + } +}; +} + + +void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) +{ + switch (colFmt) + { + case ARGB: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + + case RGB: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + } + assert(false); +} + + +bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance) +{ + switch (colFmt) + { + case ARGB: + return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + + case RGB: + return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + } + assert(false); + return false; +} + + +void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast) +{ + if (srcPitch < srcWidth * static_cast(sizeof(uint32_t)) || + trgPitch < trgWidth * static_cast(sizeof(uint32_t))) + { + assert(false); + return; + } + + switch (st) + { + case NN_SCALE_SLICE_SOURCE: + //nearest-neighbor (going over source image - fast for upscaling, since source is read only once + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) + // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight + + //keep within for loop to support MT input slices! + const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) + const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) + const int blockHeight = yTrg_last - yTrg_first; + + if (blockHeight > 0) + { + const uint32_t* srcLine = byteAdvance(src, y * srcPitch); + uint32_t* trgLine = byteAdvance(trg, yTrg_first * trgPitch); + int xTrg_first = 0; + + for (int x = 0; x < srcWidth; ++x) + { + int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; + const int blockWidth = xTrg_last - xTrg_first; + if (blockWidth > 0) + { + xTrg_first = xTrg_last; + fillBlock(trgLine, trgPitch, srcLine[x], blockWidth, blockHeight); + trgLine += blockWidth; + } + } + } + } + break; + + case NN_SCALE_SLICE_TARGET: + //nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* trgLine = byteAdvance(trg, y * trgPitch); + const int ySrc = srcHeight * y / trgHeight; + const uint32_t* srcLine = byteAdvance(src, ySrc * srcPitch); + for (int x = 0; x < trgWidth; ++x) + { + const int xSrc = srcWidth * x / trgWidth; + trgLine[x] = srcLine[xSrc]; + } + } + break; + } +} diff --git a/src/gl/xbr/xbrz.h b/src/gl/xbr/xbrz.h new file mode 100644 index 000000000..c641429e5 --- /dev/null +++ b/src/gl/xbr/xbrz.h @@ -0,0 +1,102 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#ifndef XBRZ_HEADER_3847894708239054 +#define XBRZ_HEADER_3847894708239054 + +#include //size_t +#include //uint32_t +#include +#include "xbrz_config.h" + +namespace xbrz +{ +/* +------------------------------------------------------------------------- +| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju | +------------------------------------------------------------------------- +using a modified approach of xBR: +http://board.byuu.org/viewtopic.php?f=10&t=2248 +- new rule set preserving small image features +- highly optimized for performance +- support alpha channel +- support multithreading +- support 64-bit architectures +- support processing image slices +- support scaling up to 6xBRZ +*/ + +enum ColorFormat //from high bits -> low bits, 8 bit per channel +{ + RGB, //8 bit for each red, green, blue, upper 8 bits unused + ARGB, //including alpha channel, BGRA byte order on little-endian machines +}; + +/* +-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only +-> support for source/target pitch in bytes! +-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image: + Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis) + Caveat: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition + in the target image data if you are using multiple threads for processing each enlarged slice! + +THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap! + - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process 8-16 rows at least +*/ +#ifdef max +#undef max +#endif +void scale(size_t factor, //valid range: 2 - 6 + const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, + ColorFormat colFmt, + const ScalerCfg& cfg = ScalerCfg(), + int yFirst = 0, int yLast = std::numeric_limits::max()); //slice of source image + +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight); + +enum SliceType +{ + NN_SCALE_SLICE_SOURCE, + NN_SCALE_SLICE_TARGET, +}; +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, //pitch in bytes! + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast); + +//parameter tuning +bool equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance); + + + + + +//########################### implementation ########################### +inline +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight) +{ + nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + NN_SCALE_SLICE_TARGET, 0, trgHeight); +} +} + +#endif diff --git a/src/gl/xbr/xbrz_config.h b/src/gl/xbr/xbrz_config.h new file mode 100644 index 000000000..28e9e9044 --- /dev/null +++ b/src/gl/xbr/xbrz_config.h @@ -0,0 +1,45 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#ifndef XBRZ_CONFIG_HEADER_284578425345 +#define XBRZ_CONFIG_HEADER_284578425345 + +//do NOT include any headers here! used by xBRZ_dll!!! + +namespace xbrz +{ +struct ScalerCfg +{ + ScalerCfg() : + luminanceWeight(1), + equalColorTolerance(30), + dominantDirectionThreshold(3.6), + steepDirectionThreshold(2.2), + newTestAttribute(0) {} + + double luminanceWeight; + double equalColorTolerance; + double dominantDirectionThreshold; + double steepDirectionThreshold; + double newTestAttribute; //unused; test new parameters +}; +} + +#endif \ No newline at end of file diff --git a/src/gl/xbr/xbrz_config_old.h b/src/gl/xbr/xbrz_config_old.h new file mode 100644 index 000000000..480af7976 --- /dev/null +++ b/src/gl/xbr/xbrz_config_old.h @@ -0,0 +1,45 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#ifndef __XBRZ_CONFIG_OLD_HEADER_INCLUDED__ +#define __XBRZ_CONFIG_OLD_HEADER_INCLUDED__ + +//do NOT include any headers here! used by xBRZ_dll!!! + +namespace xbrz_old +{ +struct ScalerCfg +{ + ScalerCfg() : + luminanceWeight_(1), + equalColorTolerance_(30), + dominantDirectionThreshold(3.6), + steepDirectionThreshold(2.2), + newTestAttribute_(0) {} + + double luminanceWeight_; + double equalColorTolerance_; + double dominantDirectionThreshold; + double steepDirectionThreshold; + double newTestAttribute_; //unused; test new parameters +}; +} + +#endif \ No newline at end of file diff --git a/src/gl/xbr/xbrz_old.cpp b/src/gl/xbr/xbrz_old.cpp new file mode 100644 index 000000000..07527cb95 --- /dev/null +++ b/src/gl/xbr/xbrz_old.cpp @@ -0,0 +1,1365 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#include "xbrz_old.h" + +#include +#include +#include + +#if __cplusplus > 199711 +#define XBRZ_CXX11 +#endif // __cplusplus > 199711 + +namespace +{ +template inline +unsigned char getByte(uint32_t val) { return static_cast((val >> (8 * N)) & 0xff); } + +inline unsigned char getRed (uint32_t val) { return getByte<2>(val); } +inline unsigned char getGreen(uint32_t val) { return getByte<1>(val); } +inline unsigned char getBlue (uint32_t val) { return getByte<0>(val); } + +template inline +T abs(T value) +{ +#ifdef XBRZ_CXX11 + static_assert(std::numeric_limits::is_signed, ""); +#endif // XBRZ_CXX11 + return value < 0 ? -value : value; +} + +const uint32_t redMask = 0xff0000; +const uint32_t greenMask = 0x00ff00; +const uint32_t blueMask = 0x0000ff; + +template inline +void alphaBlend(uint32_t& dst, uint32_t col) //blend color over destination with opacity N / M +{ +#ifdef XBRZ_CXX11 + static_assert(N < 256, "possible overflow of (col & redMask) * N"); + static_assert(M < 256, "possible overflow of (col & redMask ) * N + (dst & redMask ) * (M - N)"); + static_assert(0 < N && N < M, ""); +#endif // XBRZ_CXX11 + + static const uint32_t ALPHA_MASK = 0xFF000000; + static const uint32_t ALPHA_SHIFT = 24; + + static const uint32_t FULL_OPAQUE = 0xFF; + + const uint32_t colAlpha = col >> ALPHA_SHIFT; + const uint32_t dstAlpha = dst >> ALPHA_SHIFT; + + // Overflow is ignored intentionally! + + const uint32_t alpha = (FULL_OPAQUE == colAlpha && FULL_OPAQUE == dstAlpha) + ? ALPHA_MASK + : ALPHA_MASK & ((colAlpha * N + dstAlpha * (M - N)) / M << ALPHA_SHIFT); + + dst = (redMask & ((col & redMask ) * N + (dst & redMask ) * (M - N)) / M) | //this works because 8 upper bits are free + (greenMask & ((col & greenMask) * N + (dst & greenMask) * (M - N)) / M) | + (blueMask & ((col & blueMask ) * N + (dst & blueMask ) * (M - N)) / M) | + alpha; +} + + +//inline +//double fastSqrt(double n) +//{ +// __asm //speeds up xBRZ by about 9% compared to std::sqrt +// { +// fld n +// fsqrt +// } +//} +// + + +inline +uint32_t alphaBlend2(uint32_t pix1, uint32_t pix2, double alpha) +{ + return (redMask & static_cast((pix1 & redMask ) * alpha + (pix2 & redMask ) * (1 - alpha))) | + (greenMask & static_cast((pix1 & greenMask) * alpha + (pix2 & greenMask) * (1 - alpha))) | + (blueMask & static_cast((pix1 & blueMask ) * alpha + (pix2 & blueMask ) * (1 - alpha))); +} + + +uint32_t* byteAdvance( uint32_t* ptr, int bytes) { return reinterpret_cast< uint32_t*>(reinterpret_cast< char*>(ptr) + bytes); } +const uint32_t* byteAdvance(const uint32_t* ptr, int bytes) { return reinterpret_cast(reinterpret_cast(ptr) + bytes); } + + +//fill block with the given color +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int blockWidth, int blockHeight) +{ + //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + // std::fill(trg, trg + blockWidth, col); + + for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + for (int x = 0; x < blockWidth; ++x) + trg[x] = col; +} + +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int n) { fillBlock(trg, pitch, col, n, n); } + + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#elif defined __GNUC__ +#define FORCE_INLINE __attribute__((always_inline)) inline +#else +#define FORCE_INLINE inline +#endif + + +enum RotationDegree //clock-wise +{ + ROT_0, + ROT_90, + ROT_180, + ROT_270 +}; + +//calculate input matrix coordinates after rotation at compile time +template +struct MatrixRotation; + +template +struct MatrixRotation +{ + static const size_t I_old = I; + static const size_t J_old = J; +}; + +template //(i, j) = (row, col) indices, N = size of (square) matrix +struct MatrixRotation +{ + static const size_t I_old = N - 1 - MatrixRotation(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation! + static const size_t J_old = MatrixRotation(rotDeg - 1), I, J, N>::I_old; // +}; + + +template +class OutputMatrix +{ +public: + OutputMatrix(uint32_t* out, int outWidth) : //access matrix area, top-left at position "out" for image with given width + out_(out), + outWidth_(outWidth) {} + + template + uint32_t& ref() const + { + static const size_t I_old = MatrixRotation::I_old; + static const size_t J_old = MatrixRotation::J_old; + return *(out_ + J_old + I_old * outWidth_); + } + +private: + uint32_t* out_; + const int outWidth_; +}; + + +template inline +T square(T value) { return value * value; } + + +/* +inline +void rgbtoLuv(uint32_t c, double& L, double& u, double& v) +{ + //http://www.easyrgb.com/index.php?X=MATH&H=02#text2 + double r = getRed (c) / 255.0; + double g = getGreen(c) / 255.0; + double b = getBlue (c) / 255.0; + + if ( r > 0.04045 ) + r = std::pow(( ( r + 0.055 ) / 1.055 ) , 2.4); + else + r /= 12.92; + if ( g > 0.04045 ) + g = std::pow(( ( g + 0.055 ) / 1.055 ) , 2.4); + else + g /= 12.92; + if ( b > 0.04045 ) + b = std::pow(( ( b + 0.055 ) / 1.055 ) , 2.4); + else + b /= 12.92; + + r *= 100; + g *= 100; + b *= 100; + + double x = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b; + double y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b; + double z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b; + //--------------------- + double var_U = 4 * x / ( x + 15 * y + 3 * z ); + double var_V = 9 * y / ( x + 15 * y + 3 * z ); + double var_Y = y / 100; + + if ( var_Y > 0.008856 ) var_Y = std::pow(var_Y , 1.0/3 ); + else var_Y = 7.787 * var_Y + 16.0 / 116; + + const double ref_X = 95.047; //Observer= 2 degree, Illuminant= D65 + const double ref_Y = 100.000; + const double ref_Z = 108.883; + + const double ref_U = ( 4 * ref_X ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) ); + const double ref_V = ( 9 * ref_Y ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) ); + + L = ( 116 * var_Y ) - 16; + u = 13 * L * ( var_U - ref_U ); + v = 13 * L * ( var_V - ref_V ); +} +*/ + +inline +void rgbtoLab(uint32_t c, unsigned char& L, signed char& A, signed char& B) +{ + //code: http://www.easyrgb.com/index.php?X=MATH + //test: http://www.workwithcolor.com/color-converter-01.htm + //------RGB to XYZ------ + double r = getRed (c) / 255.0; + double g = getGreen(c) / 255.0; + double b = getBlue (c) / 255.0; + + r = r > 0.04045 ? std::pow(( r + 0.055 ) / 1.055, 2.4) : r / 12.92; + r = g > 0.04045 ? std::pow(( g + 0.055 ) / 1.055, 2.4) : g / 12.92; + r = b > 0.04045 ? std::pow(( b + 0.055 ) / 1.055, 2.4) : b / 12.92; + + r *= 100; + g *= 100; + b *= 100; + + double x = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b; + double y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b; + double z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b; + //------XYZ to Lab------ + const double refX = 95.047; // + const double refY = 100.000; //Observer= 2 degree, Illuminant= D65 + const double refZ = 108.883; // + double var_X = x / refX; + double var_Y = y / refY; + double var_Z = z / refZ; + + var_X = var_X > 0.008856 ? std::pow(var_X, 1.0 / 3) : 7.787 * var_X + 4.0 / 29; + var_Y = var_Y > 0.008856 ? std::pow(var_Y, 1.0 / 3) : 7.787 * var_Y + 4.0 / 29; + var_Z = var_Z > 0.008856 ? std::pow(var_Z, 1.0 / 3) : 7.787 * var_Z + 4.0 / 29; + + L = static_cast(116 * var_Y - 16); + A = static_cast< signed char>(500 * (var_X - var_Y)); + B = static_cast< signed char>(200 * (var_Y - var_Z)); +}; + + +inline +double distLAB(uint32_t pix1, uint32_t pix2) +{ + unsigned char L1 = 0; //[0, 100] + signed char a1 = 0; //[-128, 127] + signed char b1 = 0; //[-128, 127] + rgbtoLab(pix1, L1, a1, b1); + + unsigned char L2 = 0; + signed char a2 = 0; + signed char b2 = 0; + rgbtoLab(pix2, L2, a2, b2); + + //----------------------------- + //http://www.easyrgb.com/index.php?X=DELT + + //Delta E/CIE76 + return std::sqrt(square(1.0 * L1 - L2) + + square(1.0 * a1 - a2) + + square(1.0 * b1 - b2)); +} + + +/* +inline +void rgbtoHsl(uint32_t c, double& h, double& s, double& l) +{ + //http://www.easyrgb.com/index.php?X=MATH&H=18#text18 + const int r = getRed (c); + const int g = getGreen(c); + const int b = getBlue (c); + + const int varMin = numeric::min(r, g, b); + const int varMax = numeric::max(r, g, b); + const int delMax = varMax - varMin; + + l = (varMax + varMin) / 2.0 / 255.0; + + if (delMax == 0) //gray, no chroma... + { + h = 0; + s = 0; + } + else + { + s = l < 0.5 ? + delMax / (1.0 * varMax + varMin) : + delMax / (2.0 * 255 - varMax - varMin); + + double delR = ((varMax - r) / 6.0 + delMax / 2.0) / delMax; + double delG = ((varMax - g) / 6.0 + delMax / 2.0) / delMax; + double delB = ((varMax - b) / 6.0 + delMax / 2.0) / delMax; + + if (r == varMax) + h = delB - delG; + else if (g == varMax) + h = 1 / 3.0 + delR - delB; + else if (b == varMax) + h = 2 / 3.0 + delG - delR; + + if (h < 0) + h += 1; + if (h > 1) + h -= 1; + } +} + +inline +double distHSL(uint32_t pix1, uint32_t pix2, double lightningWeight) +{ + double h1 = 0; + double s1 = 0; + double l1 = 0; + rgbtoHsl(pix1, h1, s1, l1); + double h2 = 0; + double s2 = 0; + double l2 = 0; + rgbtoHsl(pix2, h2, s2, l2); + + //HSL is in cylindric coordinatates where L represents height, S radius, H angle, + //however we interpret the cylinder as a bi-conic solid with top/bottom radius 0, middle radius 1 + assert(0 <= h1 && h1 <= 1); + assert(0 <= h2 && h2 <= 1); + + double r1 = l1 < 0.5 ? + l1 * 2 : + 2 - l1 * 2; + + double x1 = r1 * s1 * std::cos(h1 * 2 * numeric::pi); + double y1 = r1 * s1 * std::sin(h1 * 2 * numeric::pi); + double z1 = l1; + + double r2 = l2 < 0.5 ? + l2 * 2 : + 2 - l2 * 2; + + double x2 = r2 * s2 * std::cos(h2 * 2 * numeric::pi); + double y2 = r2 * s2 * std::sin(h2 * 2 * numeric::pi); + double z2 = l2; + + return 255 * std::sqrt(square(x1 - x2) + square(y1 - y2) + square(lightningWeight * (z1 - z2))); +} +*/ + + +inline +double distRGB(uint32_t pix1, uint32_t pix2) +{ + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //euklidean RGB distance + return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff)); +} + + +inline +double distNonLinearRGB(uint32_t pix1, uint32_t pix2) +{ + //non-linear rgb: http://www.compuphase.com/cmetric.htm + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + const double r_avg = (static_cast(getRed(pix1)) + getRed(pix2)) / 2; + return std::sqrt((2 + r_avg / 255) * square(r_diff) + 4 * square(g_diff) + (2 + (255 - r_avg) / 255) * square(b_diff)); +} + + +inline +double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight) +{ + //http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion + //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first! + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); // + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double! + + const double k_b = 0.0722; //ITU-R BT.709 conversion + const double k_r = 0.2126; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + //we skip division by 255 to have similar range like other distance functions + return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r)); +} + + +inline +double distYUV(uint32_t pix1, uint32_t pix2, double luminanceWeight) +{ + //perf: it's not worthwhile to buffer the YUV-conversion, the direct code is faster by ~ 6% + //since RGB -> YUV conversion is essentially a matrix multiplication, we can calculate the RGB diff before the conversion (distributive property) + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //http://en.wikipedia.org/wiki/YUV#Conversion_to.2Ffrom_RGB + const double w_b = 0.114; + const double w_r = 0.299; + const double w_g = 1 - w_r - w_b; + + const double u_max = 0.436; + const double v_max = 0.615; + + const double scale_u = u_max / (1 - w_b); + const double scale_v = v_max / (1 - w_r); + + double y = w_r * r_diff + w_g * g_diff + w_b * b_diff;//value range: 255 * [-1, 1] + double u = scale_u * (b_diff - y); //value range: 255 * 2 * u_max * [-1, 1] + double v = scale_v * (r_diff - y); //value range: 255 * 2 * v_max * [-1, 1] + +#ifndef NDEBUG + const double eps = 0.5; +#endif + assert(std::abs(y) <= 255 + eps); + assert(std::abs(u) <= 255 * 2 * u_max + eps); + assert(std::abs(v) <= 255 * 2 * v_max + eps); + + return std::sqrt(square(luminanceWeight * y) + square(u) + square(v)); +} + + +inline +double colorDist(uint32_t pix1, uint32_t pix2, double luminanceWeight) +{ + if (pix1 == pix2) //about 8% perf boost + return 0; + + //return distHSL(pix1, pix2, luminanceWeight); + //return distRGB(pix1, pix2); + //return distLAB(pix1, pix2); + //return distNonLinearRGB(pix1, pix2); + //return distYUV(pix1, pix2, luminanceWeight); + + return distYCbCr(pix1, pix2, luminanceWeight); +} + + +enum BlendType +{ + BLEND_NONE = 0, + BLEND_NORMAL, //a normal indication to blend + BLEND_DOMINANT, //a strong indication to blend + //attention: BlendType must fit into the value range of 2 bit!!! +}; + +struct BlendResult +{ + BlendType + /**/blend_f, blend_g, + /**/blend_j, blend_k; +}; + + +struct Kernel_4x4 //kernel for preprocessing step +{ + uint32_t + /**/a, b, c, d, + /**/e, f, g, h, + /**/i, j, k, l, + /**/m, n, o, p; +}; + +/* +input kernel area naming convention: +----------------- +| A | B | C | D | +----|---|---|---| +| E | F | G | H | //evalute the four corners between F, G, J, K +----|---|---|---| //input pixel is at position F +| I | J | K | L | +----|---|---|---| +| M | N | O | P | +----------------- +*/ +FORCE_INLINE //detect blend direction +BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz_old::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType" +{ + BlendResult result = {}; + + if ((ker.f == ker.g && + ker.j == ker.k) || + (ker.f == ker.j && + ker.g == ker.k)) + return result; + +#ifdef XBRZ_CXX11 + auto dist = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_); }; +#else // !XBRZ_CXX11 +#define dist(C1, C2) colorDist((C1), (C2), cfg.luminanceWeight_) +#endif // XBRZ_CXX11 + + const int weight = 4; + double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g); + double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k); + +#ifndef XBRZ_CXX11 +#undef dist +#endif // !XBRZ_CXX11 + + if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8 + { + const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk; + if (ker.f != ker.g && ker.f != ker.j) + result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.k != ker.j && ker.k != ker.g) + result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + else if (fk < jg) + { + const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg; + if (ker.j != ker.f && ker.j != ker.k) + result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.g != ker.f && ker.g != ker.k) + result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + return result; +} + +struct Kernel_3x3 +{ + uint32_t + /**/a, b, c, + /**/d, e, f, + /**/g, h, i; +}; + +#define DEF_GETTER(x) template uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; } +//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token +DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c) +DEF_GETTER(d) DEF_GETTER(e) DEF_GETTER(f) +DEF_GETTER(g) DEF_GETTER(h) DEF_GETTER(i) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, g) DEF_GETTER(b, d) DEF_GETTER(c, a) +DEF_GETTER(d, h) DEF_GETTER(e, e) DEF_GETTER(f, b) +DEF_GETTER(g, i) DEF_GETTER(h, f) DEF_GETTER(i, c) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, i) DEF_GETTER(b, h) DEF_GETTER(c, g) +DEF_GETTER(d, f) DEF_GETTER(e, e) DEF_GETTER(f, d) +DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i) +DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h) +DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g) +#undef DEF_GETTER + + +//compress four blend types into a single byte +inline BlendType getTopL (unsigned char b) { return static_cast(0x3 & b); } +inline BlendType getTopR (unsigned char b) { return static_cast(0x3 & (b >> 2)); } +inline BlendType getBottomR(unsigned char b) { return static_cast(0x3 & (b >> 4)); } +inline BlendType getBottomL(unsigned char b) { return static_cast(0x3 & (b >> 6)); } + +inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing! +inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } +inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); } +inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); } + +inline bool blendingNeeded(unsigned char b) { return b != 0; } + +template inline +unsigned char rotateBlendInfo(unsigned char b) { return b; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 2) | (b >> 6)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 4) | (b >> 4)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; } + + +#ifndef NDEBUG +int debugPixelX = -1; +int debugPixelY = 84; +bool breakIntoDebugger = false; +#endif + +#define a get_a(ker) +#define b get_b(ker) +#define c get_c(ker) +#define d get_d(ker) +#define e get_e(ker) +#define f get_f(ker) +#define g get_g(ker) +#define h get_h(ker) +#define i get_i(ker) + +#ifndef XBRZ_CXX11 + +template +bool doLineBlend(const Kernel_3x3& ker, const xbrz_old::ScalerCfg& cfg, const unsigned char blend) +{ + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + +#define eq(C1, C2) (colorDist((C1), (C2), cfg.luminanceWeight_) < cfg.equalColorTolerance_) + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90 degree corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c) && !eq(e, i)) + return false; + +#undef eq + + return true; +}; + +#endif // !XBRZ_CXX11 + +/* +input kernel area naming convention: +------------- +| A | B | C | +----|---|---| +| D | E | F | //input pixel is at position E +----|---|---| +| G | H | I | +------------- +*/ +template +FORCE_INLINE //perf: quite worth it! +void scalePixel(const Kernel_3x3& ker, + uint32_t* target, int trgWidth, + unsigned char blendInfo, //result of preprocessing all four corners of pixel "e" + const xbrz_old::ScalerCfg& cfg) +{ +#ifndef NDEBUG + if (breakIntoDebugger) +#ifdef _MSC_VER + __debugbreak(); //__asm int 3; +#else // !_MSC_VER + __builtin_trap(); +#endif // _MSC_VER +#endif + + const unsigned char blend = rotateBlendInfo(blendInfo); + + if (getBottomR(blend) >= BLEND_NORMAL) + { +#ifdef XBRZ_CXX11 + auto eq = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_) < cfg.equalColorTolerance_; }; + auto dist = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_); }; + + const bool doLineBlend = [&]() -> bool + { + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90 degree corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c) && !eq(e, i)) + return false; + + return true; + }(); +#else // !XBRZ_CXX11 +#define dist(C1, C2) colorDist((C1), (C2), cfg.luminanceWeight_) +#endif // XBRZ_CXX11 + + const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color + + OutputMatrix out(target, trgWidth); + +#ifdef XBRZ_CXX11 + if (doLineBlend) +#else // !XBRZ_CXX11 + if (doLineBlend(ker, cfg, blend)) +#endif // XBRZ_CXX11 + { + const double fg = dist(f, g); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9 + const double hc = dist(h, c); // + + const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g; + const bool haveSteepLine = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c; + + if (haveShallowLine) + { + if (haveSteepLine) + Scaler::blendLineSteepAndShallow(px, out); + else + Scaler::blendLineShallow(px, out); + } + else + { + if (haveSteepLine) + Scaler::blendLineSteep(px, out); + else + Scaler::blendLineDiagonal(px,out); + } + } + else + Scaler::blendCorner(px, out); + } + +#ifndef XBRZ_CXX11 +#undef dist +#endif // XBRZ_CXX11 + +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h +#undef i +} + + +template //scaler policy: see "Scaler2x" reference implementation +void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz_old::ScalerCfg& cfg, int yFirst, int yLast) +{ + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || srcWidth <= 0) + return; + + const int trgWidth = srcWidth * Scaler::scale; + + //"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of + //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing + const int bufferSize = srcWidth; + unsigned char* preProcBuffer = reinterpret_cast(trg + yLast * Scaler::scale * trgWidth) - bufferSize; + std::fill(preProcBuffer, preProcBuffer + bufferSize, 0); +#ifdef XBRZ_CXX11 + static_assert(BLEND_NONE == 0, ""); +#endif // XBRZ_CXX11 + + //initialize preprocessing buffer for first row: detect upper left and right corner blending + //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition! + if (yFirst > 0) + { + const int y = yFirst - 1; + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + for (int x = 0; x < srcWidth; ++x) + { + const int x_m1 = std::max(x - 1, 0); + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker = {}; //perf: initialization is negligable + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //input pixel is at position F + | J | K | + --------- + */ + setTopR(preProcBuffer[x], res.blend_j); + + if (x + 1 < srcWidth) + setTopL(preProcBuffer[x + 1], res.blend_k); + } + } + //------------------------------------------------------------------------------------ + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position + + for (int x = 0; x < srcWidth; ++x, out += Scaler::scale) + { +#ifndef NDEBUG + breakIntoDebugger = debugPixelX == x && debugPixelY == y; +#endif + //all those bounds checks have only insignificant impact on performance! + const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers! + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + //evaluate the four corners on bottom-right of current pixel + unsigned char blend_xy = 0; //for current (x, y) position + { + Kernel_4x4 ker = {}; //perf: initialization is negligable + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //current input pixel is at position F + | J | K | + --------- + */ + blend_xy = preProcBuffer[x]; + setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence! + + setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1) + preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row + + blend_xy1 = 0; + setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column + + if (x + 1 < srcWidth) //set 3rd known corner for (x + 1, y) + setBottomL(preProcBuffer[x + 1], res.blend_g); + } + + //fill block of size scale * scale with the given color + fillBlock(out, trgWidth * sizeof(uint32_t), s_0[x], Scaler::scale); //place *after* preprocessing step, to not overwrite the results while processing the the last pixel! + + //blend four corners of current pixel + if (blendingNeeded(blend_xy)) //good 20% perf-improvement + { + Kernel_3x3 ker = {}; //perf: initialization is negligable + + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + + ker.d = s_0[x_m1]; + ker.e = s_0[x]; + ker.f = s_0[x_p1]; + + ker.g = s_p1[x_m1]; + ker.h = s_p1[x]; + ker.i = s_p1[x_p1]; + + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + } + } + } +} + + +struct Scaler2x +{ + static const int scale = 2; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<1, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 1>(), col); + alphaBlend<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 2>(out.template ref<1, 1>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366 + } +}; + + +struct Scaler3x +{ + static const int scale = 3; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + out.template ref<2, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<2, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 2>(), col); + alphaBlend<3, 4>(out.template ref<2, 1>(), col); + alphaBlend<3, 4>(out.template ref<1, 2>(), col); + out.template ref<2, 2>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 8>(out.template ref<1, 2>(), col); + alphaBlend<1, 8>(out.template ref<2, 1>(), col); + alphaBlend<7, 8>(out.template ref<2, 2>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598 + //alphaBlend<14, 1000>(out.template ref<2, 1>(), col); //0.01413008627 -> negligable + //alphaBlend<14, 1000>(out.template ref<1, 2>(), col); //0.01413008627 + } +}; + + +struct Scaler4x +{ + static const int scale = 4; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<3, 4>(out.template ref<3, 1>(), col); + alphaBlend<3, 4>(out.template ref<1, 3>(), col); + alphaBlend<1, 4>(out.template ref<3, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 3>(), col); + alphaBlend<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR + out.template ref<3, 3>() = out.template ref<3, 2>() = out.template ref<2, 3>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 2>(out.template ref(), col); + alphaBlend<1, 2>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563 + alphaBlend< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501 + alphaBlend< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501 + } +}; + + +struct Scaler5x +{ + static const int scale = 5; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<4, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + + out.template ref() = col; + out.template ref() = col; + + out.template ref<4, scale - 1>() = col; + + alphaBlend<2, 3>(out.template ref<3, 3>(), col); + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 8>(out.template ref(), col); + alphaBlend<1, 8>(out.template ref(), col); + alphaBlend<1, 8>(out.template ref(), col); + + alphaBlend<7, 8>(out.template ref<4, 3>(), col); + alphaBlend<7, 8>(out.template ref<3, 4>(), col); + + out.template ref<4, 4>() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088 + alphaBlend<23, 100>(out.template ref<4, 3>(), col); //0.2306749731 + alphaBlend<23, 100>(out.template ref<3, 4>(), col); //0.2306749731 + //alphaBlend<8, 1000>(out.template ref<4, 2>(), col); //0.008384061834 -> negligable + //alphaBlend<8, 1000>(out.template ref<2, 4>(), col); //0.008384061834 + } +}; +} + + +struct Scaler6x +{ + static const int scale = 6; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + alphaBlend<3, 4>(out.template ref<5, scale - 3>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 2>(out.template ref(), col); + alphaBlend<1, 2>(out.template ref(), col); + alphaBlend<1, 2>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<97, 100>(out.template ref<5, 5>(), col); //exact: 0.9711013910 + alphaBlend<42, 100>(out.template ref<4, 5>(), col); //0.4236372243 + alphaBlend<42, 100>(out.template ref<5, 4>(), col); //0.4236372243 + alphaBlend< 6, 100>(out.template ref<5, 3>(), col); //0.05652034508 + alphaBlend< 6, 100>(out.template ref<3, 5>(), col); //0.05652034508 + } +}; + + +void xbrz_old::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz_old::ScalerCfg& cfg, int yFirst, int yLast) +{ + switch (factor) + { + case 2: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + assert(false); +} + + +bool xbrz_old::equalColor(uint32_t col1, uint32_t col2, double luminanceWeight, double equalColorTolerance) +{ + return colorDist(col1, col2, luminanceWeight) < equalColorTolerance; +} + + +void xbrz_old::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast) +{ + if (srcPitch < srcWidth * static_cast(sizeof(uint32_t)) || + trgPitch < trgWidth * static_cast(sizeof(uint32_t))) + { + assert(false); + return; + } + + switch (st) + { + case NN_SCALE_SLICE_SOURCE: + //nearest-neighbor (going over source image - fast for upscaling, since source is read only once + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) + // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight + + //keep within for loop to support MT input slices! + const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) + const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) + const int blockHeight = yTrg_last - yTrg_first; + + if (blockHeight > 0) + { + const uint32_t* srcLine = byteAdvance(src, y * srcPitch); + uint32_t* trgLine = byteAdvance(trg, yTrg_first * trgPitch); + int xTrg_first = 0; + + for (int x = 0; x < srcWidth; ++x) + { + int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; + const int blockWidth = xTrg_last - xTrg_first; + if (blockWidth > 0) + { + xTrg_first = xTrg_last; + fillBlock(trgLine, trgPitch, srcLine[x], blockWidth, blockHeight); + trgLine += blockWidth; + } + } + } + } + break; + + case NN_SCALE_SLICE_TARGET: + //nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* trgLine = byteAdvance(trg, y * trgPitch); + const int ySrc = srcHeight * y / trgHeight; + const uint32_t* srcLine = byteAdvance(src, ySrc * srcPitch); + for (int x = 0; x < trgWidth; ++x) + { + const int xSrc = srcWidth * x / trgWidth; + trgLine[x] = srcLine[xSrc]; + } + } + break; + } +} diff --git a/src/gl/xbr/xbrz_old.h b/src/gl/xbr/xbrz_old.h new file mode 100644 index 000000000..c93a1480a --- /dev/null +++ b/src/gl/xbr/xbrz_old.h @@ -0,0 +1,92 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#ifndef __XBRZ_OLD_HEADER_INCLUDED__ +#define __XBRZ_OLD_HEADER_INCLUDED__ + +#include //size_t +#include //uint32_t +#include +#include "xbrz_config_old.h" + +namespace xbrz_old +{ +/* +------------------------------------------------------------------------- +| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju | +------------------------------------------------------------------------- +using a modified approach of xBR: +http://board.byuu.org/viewtopic.php?f=10&t=2248 +- new rule set preserving small image features +- support multithreading +- support 64 bit architectures +- support processing image slices +*/ + +/* +-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only +-> color format: ARGB (BGRA byte order), alpha channel unused +-> support for source/target pitch in bytes! +-> if your emulator changes only a few image slices during each cycle (e.g. Dosbox) then there's no need to run xBRZ on the complete image: + Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis) + Caveat: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition + if you are using multiple threads for processing each enlarged slice! + +THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap! + - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only + + +*/ +void scale(size_t factor, //valid range: 2 - 5 + const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, + const ScalerCfg& cfg = ScalerCfg(), + int yFirst = 0, int yLast = std::numeric_limits::max()); //slice of source image + +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight); + +enum SliceType +{ + NN_SCALE_SLICE_SOURCE, + NN_SCALE_SLICE_TARGET, +}; +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, //pitch in bytes! + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast); + +//parameter tuning +bool equalColor(uint32_t col1, uint32_t col2, double luminanceWeight, double equalColorTolerance); + + + + + +//########################### implementation ########################### +inline +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight) +{ + nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + NN_SCALE_SLICE_TARGET, 0, trgHeight); +} +} + +#endif From f31346968f447f396f190fd42cf1bfcd6162b09e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 3 Sep 2016 17:29:28 +0200 Subject: [PATCH 20/35] - added missing #include. --- src/posix/unix/i_specialpaths.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/posix/unix/i_specialpaths.cpp b/src/posix/unix/i_specialpaths.cpp index 6a17e1318..5dedba057 100644 --- a/src/posix/unix/i_specialpaths.cpp +++ b/src/posix/unix/i_specialpaths.cpp @@ -36,6 +36,7 @@ #include #include #include "i_system.h" +#include "cmdlib.h" #include "version.h" // for GAMENAME From 2ed4208a1b4ef65b339e543a4d05acc78988e329 Mon Sep 17 00:00:00 2001 From: Blue-Shadow Date: Sat, 3 Sep 2016 18:55:19 +0300 Subject: [PATCH 21/35] Added IfCVarInt SBARINFO command --- src/g_shared/sbarinfo_commands.cpp | 77 +++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index b4a011510..ba83ce1c6 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -3464,6 +3464,78 @@ class CommandIfWaterLevel : public SBarInfoNegatableFlowControl //////////////////////////////////////////////////////////////////////////////// +class CommandIfCVarInt : public SBarInfoNegatableFlowControl +{ + public: + CommandIfCVarInt(SBarInfo *script) : SBarInfoNegatableFlowControl(script), + equalcomp(false) + { + } + + void ParseNegatable(FScanner &sc, bool fullScreenOffsets) + { + if(!sc.CheckToken(TK_StringConst)) + { + sc.MustGetToken(TK_Identifier); + } + + cvarname = sc.String; + cvar = FindCVar(cvarname, nullptr); + + if (cvar != nullptr) + { + ECVarType cvartype = cvar->GetRealType(); + + if (cvartype == CVAR_Bool || cvartype == CVAR_Int) + { + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + value = sc.Number; + + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_Identifier); + + if(sc.Compare("equal")) + { + equalcomp = true; + } + } + } + else + { + sc.ScriptError("Type mismatch: console variable '%s' is not of type 'bool' or 'int'.", cvarname); + } + } + else + { + sc.ScriptError("Unknown console variable '%s'.", cvarname); + } + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); + + bool result = false; + cvar = GetCVar(statusBar->CPlayer->mo, cvarname); + + if (cvar != nullptr) + { + int cvarvalue = cvar->GetGenericRep(CVAR_Int).Int; + result = equalcomp ? cvarvalue == value : cvarvalue >= value; + } + + SetTruth(result, block, statusBar); + } + protected: + FString cvarname; + FBaseCVar *cvar; + int value; + bool equalcomp; +}; + +//////////////////////////////////////////////////////////////////////////////// + static const char *SBarInfoCommandNames[] = { "drawimage", "drawnumber", "drawswitchableimage", @@ -3474,7 +3546,7 @@ static const char *SBarInfoCommandNames[] = "isselected", "usesammo", "usessecondaryammo", "hasweaponpiece", "inventorybarnotvisible", "weaponammo", "ininventory", "alpha", "ifhealth", - "ifinvulnerable", "ifwaterlevel", + "ifinvulnerable", "ifwaterlevel", "ifcvarint", NULL }; @@ -3488,7 +3560,7 @@ enum SBarInfoCommands SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO, SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE, SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, SBARINFO_IFHEALTH, - SBARINFO_IFINVULNERABLE, SBARINFO_IFWATERLEVEL, + SBARINFO_IFINVULNERABLE, SBARINFO_IFWATERLEVEL, SBARINFO_IFCVARINT, }; SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) @@ -3524,6 +3596,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) case SBARINFO_IFHEALTH: return new CommandIfHealth(script); case SBARINFO_IFINVULNERABLE: return new CommandIfInvulnerable(script); case SBARINFO_IFWATERLEVEL: return new CommandIfWaterLevel(script); + case SBARINFO_IFCVARINT: return new CommandIfCVarInt(script); } sc.ScriptError("Unknown command '%s'.\n", sc.String); From 217601f3385d771ed657bff023b98d3ef8a7724f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Sep 2016 01:46:29 +0200 Subject: [PATCH 22/35] - fixed: FPortal::ClearScreen may not use the 2D drawing code anymore. 2D calls are accumulated and then executed all at once at the end of the frame, but this one needs to be interleaved with the 3D rendering. It now uses the quad drawer to fill the portal with blackness. --- src/gl/scene/gl_portal.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index cd3efb9e3..b613032ea 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -56,6 +56,7 @@ #include "gl/renderer/gl_lightdata.h" #include "gl/renderer/gl_renderer.h" #include "gl/renderer/gl_renderstate.h" +#include "gl/renderer/gl_quaddrawer.h" #include "gl/dynlights/gl_glow.h" #include "gl/data/gl_data.h" #include "gl/data/gl_vertexbuffer.h" @@ -128,8 +129,21 @@ void GLPortal::ClearScreen() bool multi = !!glIsEnabled(GL_MULTISAMPLE); gl_MatrixStack.Push(gl_RenderState.mViewMatrix); gl_MatrixStack.Push(gl_RenderState.mProjectionMatrix); - screen->Begin2D(false); - screen->Dim(0, 1.f, 0, 0, SCREENWIDTH, SCREENHEIGHT); + + gl_RenderState.mViewMatrix.loadIdentity(); + gl_RenderState.mProjectionMatrix.ortho(0, SCREENWIDTH, SCREENHEIGHT, 0, -1.0f, 1.0f); + gl_RenderState.ApplyMatrices(); + + glDisable(GL_MULTISAMPLE); + glDisable(GL_DEPTH_TEST); + + FQuadDrawer qd; + qd.Set(0, 0, 0, 0, 0, 0); + qd.Set(1, 0, SCREENHEIGHT, 0, 0, 0); + qd.Set(2, SCREENWIDTH, SCREENHEIGHT, 0, 0, 0); + qd.Set(3, SCREENWIDTH, 0, 0, 0, 0); + qd.Render(GL_TRIANGLE_FAN); + glEnable(GL_DEPTH_TEST); gl_MatrixStack.Pop(gl_RenderState.mProjectionMatrix); gl_MatrixStack.Pop(gl_RenderState.mViewMatrix); @@ -137,7 +151,6 @@ void GLPortal::ClearScreen() if (multi) glEnable(GL_MULTISAMPLE); } - //----------------------------------------------------------------------------- // // DrawPortalStencil From dc39a006dc3d58182d2dba83a4ac50258799c9f6 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 4 Sep 2016 02:37:59 +0200 Subject: [PATCH 23/35] Fix palette tonemap precision and compile error on Intel --- src/gl/renderer/gl_postprocess.cpp | 2 +- wadsrc/static/shaders/glsl/tonemap.fp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 77ee5cd79..645bc3fe1 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -257,7 +257,7 @@ void FGLRenderer::BindTonemapPalette(int texunit) { for (int b = 0; b < 64; b++) { - PalEntry color = GPalette.BaseColors[ColorMatcher.Pick((r << 2) | (r >> 1), (g << 2) | (g >> 1), (b << 2) | (b >> 1))]; + PalEntry color = GPalette.BaseColors[ColorMatcher.Pick((r << 2) | (r >> 4), (g << 2) | (g >> 4), (b << 2) | (b >> 4))]; int index = ((r * 64 + g) * 64 + b) * 4; lut[index] = color.r; lut[index + 1] = color.g; diff --git a/wadsrc/static/shaders/glsl/tonemap.fp b/wadsrc/static/shaders/glsl/tonemap.fp index c33349a38..d6574b295 100644 --- a/wadsrc/static/shaders/glsl/tonemap.fp +++ b/wadsrc/static/shaders/glsl/tonemap.fp @@ -69,15 +69,15 @@ uniform sampler2D PaletteLUT; vec3 Tonemap(vec3 color) { - ivec3 c = ivec3(clamp(color.rgb, vec3(0.0), vec3(1.0)) * 255.0 + 0.5); - int index = ((c.r >> 2) * 64 + (c.g >> 2)) * 64 + (c.b >> 2); + ivec3 c = ivec3(clamp(color.rgb, vec3(0.0), vec3(1.0)) * 63.0 + 0.5); + int index = (c.r * 64 + c.g) * 64 + c.b; int tx = index % 512; int ty = index / 512; return texelFetch(PaletteLUT, ivec2(tx, ty), 0).rgb; } #else -#error "Tonemap mode define is missing" +#error Tonemap mode define is missing #endif void main() From 5f02e08c8e133f93d9e9e72e004e662d15ef8b7d Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 4 Sep 2016 03:15:50 +0200 Subject: [PATCH 24/35] Fix minimize crash --- src/gl/renderer/gl_postprocess.cpp | 2 ++ src/gl/renderer/gl_renderer.cpp | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 645bc3fe1..e6ca7faad 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -444,6 +444,8 @@ void FGLRenderer::ClearBorders() int clientWidth = framebuffer->GetClientWidth(); int clientHeight = framebuffer->GetClientHeight(); + if (clientWidth == 0 || clientHeight == 0) + return; glViewport(0, 0, clientWidth, clientHeight); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 6cd38390f..f9d627d93 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -231,6 +231,13 @@ void FGLRenderer::SetOutputViewport(GL_IRECT *bounds) // Back buffer letterbox for the final output int clientWidth = framebuffer->GetClientWidth(); int clientHeight = framebuffer->GetClientHeight(); + if (clientWidth == 0 || clientHeight == 0) + { + // When window is minimized there may not be any client area. + // Pretend to the rest of the render code that we just have a very small window. + clientWidth = 160; + clientHeight = 120; + } int screenWidth = framebuffer->GetWidth(); int screenHeight = framebuffer->GetHeight(); float scale = MIN(clientWidth / (float)screenWidth, clientHeight / (float)screenHeight); From 527703ae8c2612a63925e4e7296df49f9ea3af22 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 4 Sep 2016 03:21:47 +0200 Subject: [PATCH 25/35] Fix missing flash if multisampling was on and no post processing effects active --- src/gl/scene/gl_scene.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index b4dd95d50..23d7c7c80 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -856,7 +856,11 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo TonemapScene(); ColormapScene(); LensDistortScene(); - DrawBlend(viewsector); // This should be done after postprocessing, not before. + + // This should be done after postprocessing, not before. + mBuffers->BindCurrentFB(); + glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + DrawBlend(viewsector); } mDrawingScene2D = false; eye->TearDown(); From 77ac3bb2655ec922d62159ea93db73aaae8701e3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Sep 2016 08:33:19 +0200 Subject: [PATCH 26/35] - fixed angle range checks in A_CheckIfTargetInLOS. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fixed point version had a mostly useless check that excluded ANGLE_MAX, this got incorrectly converted to floating point. Note that this version will clamp the angle to 360°, not merely overflow like it did with the fixed point code --- src/thingdef/thingdef_codeptr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 1f24a35c7..83db1fdaa 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -4501,7 +4501,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) else { target = viewport; viewport = self; } } - if (fov > 0 && (fov < 360.)) + fov = MIN(fov, 360.); + + if (fov > 0) { DAngle an = absangle(viewport->AngleTo(target), viewport->Angles.Yaw); From 677efb73bce2b178b90781afbba1f9b3912f2206 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 4 Sep 2016 10:02:09 +0300 Subject: [PATCH 27/35] Fixed compilation with GCC/Clang No longer aborts with error: cannot pass object of non-trivial type 'FString' through variadic method; call will abort at runtime --- src/g_shared/sbarinfo_commands.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index ba83ce1c6..a9fac2ee1 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -3504,12 +3504,12 @@ class CommandIfCVarInt : public SBarInfoNegatableFlowControl } else { - sc.ScriptError("Type mismatch: console variable '%s' is not of type 'bool' or 'int'.", cvarname); + sc.ScriptError("Type mismatch: console variable '%s' is not of type 'bool' or 'int'.", cvarname.GetChars()); } } else { - sc.ScriptError("Unknown console variable '%s'.", cvarname); + sc.ScriptError("Unknown console variable '%s'.", cvarname.GetChars()); } } void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) From eff03d13f0e4b54c7ac1fe0f942f1bc3580c7ddf Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Sep 2016 10:22:59 +0200 Subject: [PATCH 28/35] - fixed last commit. --- src/thingdef/thingdef_codeptr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 83db1fdaa..d520a0b9d 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -76,6 +76,7 @@ #include "d_player.h" #include "p_maputl.h" #include "p_spec.h" +#include "templates.h" #include "math/cmath.h" AActor *SingleActorFromTID(int tid, AActor *defactor); @@ -4501,7 +4502,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) else { target = viewport; viewport = self; } } - fov = MIN(fov, 360.); + fov = MIN(fov, 360.); if (fov > 0) { From 954ac8ce5e607e7811a91717e0e1cfe5126d820a Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Sep 2016 10:27:41 +0200 Subject: [PATCH 29/35] - removed DirectX setup from CMakeLists for Visual Studio For VS 2015 this is no longer needed, the DX headers and libraries are part of the Windows SDK and do not need to be looked for explicitly. --- src/CMakeLists.txt | 84 ++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51548afdd..190a9560a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -114,49 +114,59 @@ if( WIN32 ) set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib ) set( NASM_NAMES nasmw nasm ) - find_path( D3D_INCLUDE_DIR d3d9.h - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Include ) - if( NOT D3D_INCLUDE_DIR ) - message( SEND_ERROR "Could not find DirectX 9 header files" ) - else() - include_directories( ${D3D_INCLUDE_DIR} ) - endif() + if( NOT MSVC ) + find_path( D3D_INCLUDE_DIR d3d9.h + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Include ) + if( NOT D3D_INCLUDE_DIR ) + message( SEND_ERROR "Could not find DirectX 9 header files" ) + else() + include_directories( ${D3D_INCLUDE_DIR} ) + endif() - find_path( XINPUT_INCLUDE_DIR xinput.h - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Include ) - if( NOT XINPUT_INCLUDE_DIR ) - message( WARNING "Could not find xinput.h. XInput will be disabled." ) - add_definitions( -DNO_XINPUT ) + find_path( XINPUT_INCLUDE_DIR xinput.h + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Include ) + if( NOT XINPUT_INCLUDE_DIR ) + message( WARNING "Could not find xinput.h. XInput will be disabled." ) + add_definitions( -DNO_XINPUT ) + else() + include_directories( ${XINPUT_INCLUDE_DIR} ) + endif() + + find_library( DX_dxguid_LIBRARY dxguid + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Lib Lib/${XBITS} ) + find_library( DX_dinput8_LIBRARY dinput8 + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Lib Lib/${XBITS} ) + + set( DX_LIBS_FOUND YES ) + if( NOT DX_dxguid_LIBRARY ) + set( DX_LIBS_FOUND NO ) + endif() + if( NOT DX_dinput8_LIBRARY ) + set( DX_LIBS_FOUND NO ) + endif() + + if( NOT DX_LIBS_FOUND ) + message( FATAL_ERROR "Could not find DirectX 9 libraries" ) + endif() + + set( DX_LIBS + "${DX_dxguid_LIBRARY}" + "${DX_dinput8_LIBRARY}" + ) else() - include_directories( ${XINPUT_INCLUDE_DIR} ) + set( DX_LIBS + dxguid + dinput8 + ) endif() - find_library( DX_dxguid_LIBRARY dxguid - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Lib Lib/${XBITS} ) - find_library( DX_dinput8_LIBRARY dinput8 - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Lib Lib/${XBITS} ) - - set( DX_LIBS_FOUND YES ) - if( NOT DX_dxguid_LIBRARY ) - set( DX_LIBS_FOUND NO ) - endif() - if( NOT DX_dinput8_LIBRARY ) - set( DX_LIBS_FOUND NO ) - endif() - - if( NOT DX_LIBS_FOUND ) - message( FATAL_ERROR "Could not find DirectX 9 libraries" ) - endif() - - set( ZDOOM_LIBS + set( ZDOOM_LIBS ${DX_LIBS} wsock32 winmm - "${DX_dxguid_LIBRARY}" - "${DX_dinput8_LIBRARY}" ole32 user32 gdi32 From e7856ce1e354da13f2b5ffd9795b0bfb1586e37e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Sep 2016 12:35:26 +0200 Subject: [PATCH 30/35] - removed unused forceadditive parameter from gl_GetLight. - restricted gl_lights_additive to legacy code and removed menu entry for this. For modern hardware this setting is completely pointless, it offers no advantage and degrades visual quality. Its only reason for existence was that drawing additive lights with textures is a lot faster, and that's all it's being used for now. --- src/gl/compatibility/gl_20.cpp | 4 +++- src/gl/dynlights/a_dynlight.cpp | 1 - src/gl/dynlights/gl_dynlight.h | 2 +- src/gl/dynlights/gl_dynlight1.cpp | 9 ++------- src/gl/scene/gl_flats.cpp | 2 +- src/gl/scene/gl_walls_draw.cpp | 2 +- src/gl/system/gl_cvars.h | 1 - wadsrc/static/menudef.z | 1 - 8 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp index fdda99130..60dd9ecdb 100644 --- a/src/gl/compatibility/gl_20.cpp +++ b/src/gl/compatibility/gl_20.cpp @@ -57,6 +57,8 @@ #include "gl/data/gl_vertexbuffer.h" +CVAR(Bool, gl_lights_additive, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + //========================================================================== // // Do some tinkering with the menus so that certain options only appear @@ -478,7 +480,7 @@ bool GLWall::PutWallCompat(int passflag) if (sub->lighthead == nullptr) return false; } - bool foggy = !gl_isBlack(Colormap.FadeColor) || (level.flags&LEVEL_HASFADETABLE) || gl_lights_additive; + bool foggy = gl_CheckFog(&Colormap, lightlevel) || (level.flags&LEVEL_HASFADETABLE) || gl_lights_additive; bool masked = passflag == 2 && gltexture->isMasked(); int list = list_indices[masked][foggy]; diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp index ef96fdd3e..e90bbdc80 100644 --- a/src/gl/dynlights/a_dynlight.cpp +++ b/src/gl/dynlights/a_dynlight.cpp @@ -58,7 +58,6 @@ #include "gl/utility/gl_templates.h" EXTERN_CVAR (Float, gl_lights_size); -EXTERN_CVAR (Bool, gl_lights_additive); EXTERN_CVAR(Int, vid_renderer) diff --git a/src/gl/dynlights/gl_dynlight.h b/src/gl/dynlights/gl_dynlight.h index 513245be5..6b6e40c2d 100644 --- a/src/gl/dynlights/gl_dynlight.h +++ b/src/gl/dynlights/gl_dynlight.h @@ -184,7 +184,7 @@ struct FDynLightData -bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, bool forceadditive, FDynLightData &data); +bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FDynLightData &data); void gl_UploadLights(FDynLightData &data); diff --git a/src/gl/dynlights/gl_dynlight1.cpp b/src/gl/dynlights/gl_dynlight1.cpp index 361b94618..eb3c45f9c 100644 --- a/src/gl/dynlights/gl_dynlight1.cpp +++ b/src/gl/dynlights/gl_dynlight1.cpp @@ -74,18 +74,13 @@ CVAR (Float, gl_lights_intensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Float, gl_lights_size, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_light_sprites, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_light_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -CUSTOM_CVAR (Bool, gl_lights_additive, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) -{ - gl_DeleteAllAttachedLights(); - gl_RecreateAllAttachedLights(); -} //========================================================================== // // Sets up the parameters to render one dynamic light onto one plane // //========================================================================== -bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, bool forceadditive, FDynLightData &ldata) +bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FDynLightData &ldata) { int i = 0; @@ -103,7 +98,7 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, bo float cs; - if (gl_lights_additive || light->flags4&MF4_ADDITIVE || forceadditive) + if (light->IsAdditive()) { cs = 0.2f; i = 2; diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index 1a3fe994b..9e9a73e5d 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -154,7 +154,7 @@ void GLFlat::SetupSubsectorLights(int pass, subsector_t * sub, int *dli) } p.Set(plane.plane); - gl_GetLight(sub->sector->PortalGroup, p, light, false, false, lightdata); + gl_GetLight(sub->sector->PortalGroup, p, light, false, lightdata); node = node->nextLight; } diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 40145abc4..9599b8fa3 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -158,7 +158,7 @@ void GLWall::SetupLights() } if (outcnt[0]!=4 && outcnt[1]!=4 && outcnt[2]!=4 && outcnt[3]!=4) { - gl_GetLight(seg->frontsector->PortalGroup, p, node->lightsource, true, false, lightdata); + gl_GetLight(seg->frontsector->PortalGroup, p, node->lightsource, true, lightdata); } } } diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index 0c31f53a8..da1febe3c 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -26,7 +26,6 @@ EXTERN_CVAR (Bool, gl_attachedlights); EXTERN_CVAR (Bool, gl_lights_checkside); EXTERN_CVAR (Float, gl_lights_intensity); EXTERN_CVAR (Float, gl_lights_size); -EXTERN_CVAR (Bool, gl_lights_additive); EXTERN_CVAR (Bool, gl_light_sprites); EXTERN_CVAR (Bool, gl_light_particles); diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index 86e2d5dc0..abc7cfacc 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -198,7 +198,6 @@ OptionMenu "GLLightOptions" Option "$GLLIGHTMNU_CLIPLIGHTS", gl_lights_checkside, "YesNo" Option "$GLLIGHTMNU_LIGHTSPRITES", gl_light_sprites, "YesNo" Option "$GLLIGHTMNU_LIGHTPARTICLES", gl_light_particles, "YesNo" - Option "$GLLIGHTMNU_FORCEADDITIVE", gl_lights_additive, "YesNo" Slider "$GLLIGHTMNU_LIGHTINTENSITY", gl_lights_intensity, 0.0, 1.0, 0.1 Slider "$GLLIGHTMNU_LIGHTSIZE", gl_lights_size, 0.0, 2.0, 0.1 } From 8b01a88b76f815833f77b3b7a7fcc47159d471e0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Sep 2016 12:45:09 +0200 Subject: [PATCH 31/35] - removed gl_lights_size and gl_lights_intensity. Both of these were inherited from ZDoomGL and in terms of light design in maps it makes absolutely no sense to have them user configurable. They should have been removed 11 years ago. --- src/gl/compatibility/gl_20.cpp | 8 ++++---- src/gl/dynlights/a_dynlight.cpp | 3 +-- src/gl/dynlights/gl_dynlight.cpp | 4 +--- src/gl/dynlights/gl_dynlight1.cpp | 10 ++++------ src/gl/scene/gl_spritelight.cpp | 8 ++++---- src/gl/scene/gl_walls_draw.cpp | 2 +- src/gl/system/gl_cvars.h | 2 -- wadsrc/static/menudef.z | 2 -- 8 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp index 60dd9ecdb..e192b213e 100644 --- a/src/gl/compatibility/gl_20.cpp +++ b/src/gl/compatibility/gl_20.cpp @@ -386,7 +386,7 @@ bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, DVector3 lpos = light->PosRelative(group); float dist = fabsf(p.DistToPoint(lpos.X, lpos.Z, lpos.Y)); - float radius = (light->GetRadius() * gl_lights_size); + float radius = light->GetRadius(); if (radius <= 0.f) return false; if (dist > radius) return false; @@ -417,9 +417,9 @@ bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, float cs = 1.0f - (dist / radius); if (additive) cs *= 0.2f; // otherwise the light gets too strong. - float r = light->GetRed() / 255.0f * cs * gl_lights_intensity; - float g = light->GetGreen() / 255.0f * cs * gl_lights_intensity; - float b = light->GetBlue() / 255.0f * cs * gl_lights_intensity; + float r = light->GetRed() / 255.0f * cs; + float g = light->GetGreen() / 255.0f * cs; + float b = light->GetBlue() / 255.0f * cs; if (light->IsSubtractive()) { diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp index e90bbdc80..ac929e7c0 100644 --- a/src/gl/dynlights/a_dynlight.cpp +++ b/src/gl/dynlights/a_dynlight.cpp @@ -57,7 +57,6 @@ #include "gl/utility/gl_convert.h" #include "gl/utility/gl_templates.h" -EXTERN_CVAR (Float, gl_lights_size); EXTERN_CVAR(Int, vid_renderer) @@ -378,7 +377,7 @@ void ADynamicLight::UpdateLocation() { intensity = m_currentRadius; } - radius = intensity * 2.0f * gl_lights_size; + radius = intensity * 2.0f; if (X() != oldx || Y() != oldy || radius != oldradius) { diff --git a/src/gl/dynlights/gl_dynlight.cpp b/src/gl/dynlights/gl_dynlight.cpp index e7d876b2a..b12290ab6 100644 --- a/src/gl/dynlights/gl_dynlight.cpp +++ b/src/gl/dynlights/gl_dynlight.cpp @@ -61,8 +61,6 @@ #include "gl/utility/gl_clock.h" #include "gl/utility/gl_convert.h" -EXTERN_CVAR (Float, gl_lights_intensity); -EXTERN_CVAR (Float, gl_lights_size); int ScriptDepth; void gl_InitGlow(FScanner &sc); void gl_ParseBrightmap(FScanner &sc, int); @@ -175,7 +173,7 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const light->Angles.Yaw.Degrees = m_Param; light->SetOffset(m_Pos); light->halo = m_halo; - for (int a = 0; a < 3; a++) light->args[a] = clamp((int)(m_Args[a] * gl_lights_intensity), 0, 255); + for (int a = 0; a < 3; a++) light->args[a] = clamp((int)(m_Args[a]), 0, 255); light->m_Radius[0] = int(m_Args[LIGHT_INTENSITY]); light->m_Radius[1] = int(m_Args[LIGHT_SECONDARY_INTENSITY]); light->flags4 &= ~(MF4_ADDITIVE | MF4_SUBTRACTIVE | MF4_DONTLIGHTSELF); diff --git a/src/gl/dynlights/gl_dynlight1.cpp b/src/gl/dynlights/gl_dynlight1.cpp index eb3c45f9c..89cdd1ecb 100644 --- a/src/gl/dynlights/gl_dynlight1.cpp +++ b/src/gl/dynlights/gl_dynlight1.cpp @@ -70,8 +70,6 @@ CUSTOM_CVAR (Bool, gl_lights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOIN CVAR (Bool, gl_attachedlights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_lights_checkside, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -CVAR (Float, gl_lights_intensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -CVAR (Float, gl_lights_size, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_light_sprites, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_light_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); @@ -87,7 +85,7 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FD DVector3 pos = light->PosRelative(group); float dist = fabsf(p.DistToPoint(pos.X, pos.Z, pos.Y)); - float radius = (light->GetRadius() * gl_lights_size); + float radius = (light->GetRadius()); if (radius <= 0.f) return false; if (dist > radius) return false; @@ -108,9 +106,9 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FD cs = 1.0f; } - float r = light->GetRed() / 255.0f * cs * gl_lights_intensity; - float g = light->GetGreen() / 255.0f * cs * gl_lights_intensity; - float b = light->GetBlue() / 255.0f * cs * gl_lights_intensity; + float r = light->GetRed() / 255.0f * cs; + float g = light->GetGreen() / 255.0f * cs; + float b = light->GetBlue() / 255.0f * cs; if (light->IsSubtractive()) { diff --git a/src/gl/scene/gl_spritelight.cpp b/src/gl/scene/gl_spritelight.cpp index b25fc30f0..329459bd1 100644 --- a/src/gl/scene/gl_spritelight.cpp +++ b/src/gl/scene/gl_spritelight.cpp @@ -96,7 +96,7 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * dist = FVector3(x - light->X(), y - light->Y(), z - light->Z()).LengthSquared(); } - radius = light->GetRadius() * gl_lights_size; + radius = light->GetRadius(); if (dist < radius * radius) { @@ -106,9 +106,9 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * if (frac > 0) { - lr = light->GetRed() / 255.0f * gl_lights_intensity; - lg = light->GetGreen() / 255.0f * gl_lights_intensity; - lb = light->GetBlue() / 255.0f * gl_lights_intensity; + lr = light->GetRed() / 255.0f; + lg = light->GetGreen() / 255.0f; + lb = light->GetBlue() / 255.0f; if (light->IsSubtractive()) { float bright = FVector3(lr, lg, lb).Length(); diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 9599b8fa3..9f521ad11 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -124,7 +124,7 @@ void GLWall::SetupLights() float y = node->lightsource->Y(); float z = node->lightsource->Z(); float dist = fabsf(p.DistToPoint(x, z, y)); - float radius = (node->lightsource->GetRadius() * gl_lights_size); + float radius = node->lightsource->GetRadius(); float scale = 1.0f / ((2.f * radius) - dist); if (radius > 0.f && dist < radius) diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index da1febe3c..4cda3657d 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -24,8 +24,6 @@ EXTERN_CVAR(Int, gl_weaponlight) EXTERN_CVAR (Bool, gl_lights); EXTERN_CVAR (Bool, gl_attachedlights); EXTERN_CVAR (Bool, gl_lights_checkside); -EXTERN_CVAR (Float, gl_lights_intensity); -EXTERN_CVAR (Float, gl_lights_size); EXTERN_CVAR (Bool, gl_light_sprites); EXTERN_CVAR (Bool, gl_light_particles); diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index abc7cfacc..8879d6170 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -198,8 +198,6 @@ OptionMenu "GLLightOptions" Option "$GLLIGHTMNU_CLIPLIGHTS", gl_lights_checkside, "YesNo" Option "$GLLIGHTMNU_LIGHTSPRITES", gl_light_sprites, "YesNo" Option "$GLLIGHTMNU_LIGHTPARTICLES", gl_light_particles, "YesNo" - Slider "$GLLIGHTMNU_LIGHTINTENSITY", gl_lights_intensity, 0.0, 1.0, 0.1 - Slider "$GLLIGHTMNU_LIGHTSIZE", gl_lights_size, 0.0, 2.0, 0.1 } OptionMenu "GLPrefOptions" From 95bedac6ca8240e010bb9ff49168fdd4abde06e7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Sep 2016 13:14:14 +0200 Subject: [PATCH 32/35] - inlined FHardwareTexture::GetTexDimension. --- src/gl/textures/gl_hwtexture.cpp | 12 ------------ src/gl/textures/gl_hwtexture.h | 7 ++++++- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/gl/textures/gl_hwtexture.cpp b/src/gl/textures/gl_hwtexture.cpp index 73c4e7b36..25dc989e7 100644 --- a/src/gl/textures/gl_hwtexture.cpp +++ b/src/gl/textures/gl_hwtexture.cpp @@ -64,18 +64,6 @@ extern int TexFormat[]; //=========================================================================== unsigned int FHardwareTexture::lastbound[FHardwareTexture::MAX_TEXTURES]; -//=========================================================================== -// -// STATIC - Gets the maximum size of hardware textures -// -//=========================================================================== -int FHardwareTexture::GetTexDimension(int value) -{ - if (value > gl.max_texturesize) return gl.max_texturesize; - return value; -} - - //=========================================================================== // // Quick'n dirty image rescaling. diff --git a/src/gl/textures/gl_hwtexture.h b/src/gl/textures/gl_hwtexture.h index 4c00af272..96eff0264 100644 --- a/src/gl/textures/gl_hwtexture.h +++ b/src/gl/textures/gl_hwtexture.h @@ -10,6 +10,7 @@ #define DIRECT_PALETTE -2 #include "tarray.h" +#include "gl/system/gl_interface.h" class FCanvasTexture; class AActor; @@ -49,7 +50,11 @@ public: static unsigned int lastbound[MAX_TEXTURES]; - static int GetTexDimension(int value); + static int GetTexDimension(int value) + { + if (value > gl.max_texturesize) return gl.max_texturesize; + return value; + } static void InitGlobalState() { for (int i = 0; i < MAX_TEXTURES; i++) lastbound[i] = 0; } From d2ead39bccb393f8496e4ba9176f464ec5b4bf3f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Sep 2016 14:16:05 +0200 Subject: [PATCH 33/35] - force framebuffers for camera textures on GL 3+ hardware. With all the postprocessing stuff added I don't think it's ok to use the screenbuffer for this anymore. - disable framebuffers for camera textures in legacy mode entirely. This depends on a GL_DEPTH24_STENCIL8 surface which most of these old chipsets do not support, and I really see no point to invest any work here. The worst that can happen is that oversized camera textures won't be processed, which, due to general performance issues, might even be a good thing. --- src/gl/scene/gl_scene.cpp | 25 ++++++------------------- wadsrc/static/language.enu | 4 ---- wadsrc/static/menudef.z | 1 - 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 23d7c7c80..8a2764829 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -1317,29 +1317,16 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in gl_fixedcolormap=CM_DEFAULT; gl_RenderState.SetFixedColormap(CM_DEFAULT); - bool usefb = !gl.legacyMode || gl_usefb || gltex->GetWidth() > screen->GetWidth() || gltex->GetHeight() > screen->GetHeight(); - if (!usefb) + if (gl.legacyMode) { + // In legacy mode, fail if the requested texture is too large. + if (gltex->GetWidth() > screen->GetWidth() || gltex->GetHeight() > screen->GetHeight()) return; glFlush(); } else { -#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) - __try -#endif - { - GLRenderer->StartOffscreen(); - gltex->BindToFrameBuffer(); - } -#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) - __except(1) - { - usefb = false; - gl_usefb = false; - GLRenderer->EndOffscreen(); - glFlush(); - } -#endif + GLRenderer->StartOffscreen(); + gltex->BindToFrameBuffer(); } GL_IRECT bounds; @@ -1349,7 +1336,7 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in GLRenderer->RenderViewpoint(Viewpoint, &bounds, FOV, (float)width/height, (float)width/height, false, false); - if (!usefb) + if (gl.legacyMode) { glFlush(); gl_RenderState.SetMaterial(gltex, 0, 0, -1, false); diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index d2da033d7..c7d19fd92 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2594,7 +2594,6 @@ GLTEXMNU_RESIZETEX = "Resize textures"; GLTEXMNU_RESIZESPR = "Resize sprites"; GLTEXMNU_RESIZEFNT = "Resize fonts"; GLTEXMNU_PRECACHETEX = "Precache GL textures"; -GLTEXMNU_CAMTEXOFFSCR = "Camera textures offscreen"; GLTEXMNU_TRIMSPREDGE = "Trim sprite edges"; GLTEXMNU_SORTDRAWLIST = "Sort draw lists by texture"; @@ -2605,9 +2604,6 @@ GLLIGHTMNU_LIGHTDEFS = "Enable light definitions"; GLLIGHTMNU_CLIPLIGHTS = "Clip lights"; GLLIGHTMNU_LIGHTSPRITES = "Lights affect sprites"; GLLIGHTMNU_LIGHTPARTICLES = "Lights affect particles"; -GLLIGHTMNU_FORCEADDITIVE = "Force additive lighting"; -GLLIGHTMNU_LIGHTINTENSITY = "Light intensity"; -GLLIGHTMNU_LIGHTSIZE = "Light size"; // OpenGL Preferences GLPREFMNU_TITLE = "OPENGL PREFERENCES"; diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index 8879d6170..980cdacc3 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -185,7 +185,6 @@ OptionMenu "GLTextureGLOptions" Option "$GLTEXMNU_RESIZESPR", gl_texture_hqresize_sprites, "OnOff" Option "$GLTEXMNU_RESIZEFNT", gl_texture_hqresize_fonts, "OnOff" Option "$GLTEXMNU_PRECACHETEX", gl_precache, "YesNo" - Option "$GLTEXMNU_CAMTEXOFFSCR", gl_usefb, "OnOff" Option "$GLTEXMNU_TRIMSPREDGE", gl_trimsprites, "OnOff" Option "$GLTEXMNU_SORTDRAWLIST", gl_sort_textures, "YesNo" } From 108dcf122ae02d0354742081e9f67aa791831f7d Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sun, 4 Sep 2016 15:23:29 +0300 Subject: [PATCH 34/35] Updated support for legacy renderer in Cocoa backend Added fallback to legacy profile when creation of pixel format for core context failed Added handling of -glversion command line switch --- src/posix/cocoa/i_video.mm | 71 ++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index f455a5f9f..624e8ad07 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -459,23 +459,14 @@ CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) return window; } -} // unnamed namespace - - -// --------------------------------------------------------------------------- - - -CocoaVideo::CocoaVideo() -: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) -, m_width(-1) -, m_height(-1) -, m_fullscreen(false) -, m_hiDPI(false) +enum OpenGLProfile { - memset(&m_modeIterator, 0, sizeof m_modeIterator); - - // Set attributes for OpenGL context + Core, + Legacy +}; +NSOpenGLPixelFormat* CreatePixelFormat(const OpenGLProfile profile) +{ NSOpenGLPixelFormatAttribute attributes[16]; size_t i = 0; @@ -492,17 +483,59 @@ CocoaVideo::CocoaVideo() attributes[i++] = NSOpenGLPFAAllowOfflineRenderers; } - if (NSAppKitVersionNumber >= AppKit10_7) + if (NSAppKitVersionNumber >= AppKit10_7 && OpenGLProfile::Core == profile) { + NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersion3_2Core; + const char* const glversion = Args->CheckValue("-glversion"); + + if (nullptr != glversion) + { + const double version = strtod(glversion, nullptr) + 0.01; + if (version < 3.2) + { + profile = NSOpenGLProfileVersionLegacy; + } + } + attributes[i++] = NSOpenGLPFAOpenGLProfile; - attributes[i++] = NSOpenGLProfileVersion3_2Core; + attributes[i++] = profile; } attributes[i] = NSOpenGLPixelFormatAttribute(0); - // Create OpenGL context and view + return [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; +} - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +CocoaVideo::CocoaVideo() +: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) +, m_width(-1) +, m_height(-1) +, m_fullscreen(false) +, m_hiDPI(false) +{ + memset(&m_modeIterator, 0, sizeof m_modeIterator); + + // Create OpenGL pixel format + + NSOpenGLPixelFormat* pixelFormat = CreatePixelFormat(OpenGLProfile::Core); + + if (nil == pixelFormat) + { + pixelFormat = CreatePixelFormat(OpenGLProfile::Legacy); + + if (nil == pixelFormat) + { + I_FatalError("Cannot OpenGL create pixel format, graphics hardware is not supported"); + } + } + + // Create OpenGL context and view const NSRect contentRect = [m_window contentRectForFrameRect:[m_window frame]]; NSOpenGLView* glView = [[CocoaView alloc] initWithFrame:contentRect From 74ede7bb4ee92aa5ad14494f689fc81e75f60d05 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 5 Sep 2016 09:07:34 +0200 Subject: [PATCH 35/35] - fixed: The DrawBlend call in the postprocessing case was using the global 'viewsector' variable directly. As with the Stereo3D stuff, this cannot be done because recursive processing of portals will change it. Instead the local copy has to be used here, just like the EndDrawScene call already did. --- src/gl/scene/gl_scene.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 8a2764829..48d4fb7ff 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -771,7 +771,7 @@ void FGLRenderer::SetFixedColormap (player_t *player) sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) { - sector_t * retval; + sector_t * lviewsector; mSceneClearColor[0] = 0.0f; mSceneClearColor[1] = 0.0f; mSceneClearColor[2] = 0.0f; @@ -818,7 +818,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo } // 'viewsector' will not survive the rendering so it cannot be used anymore below. - retval = viewsector; + lviewsector = viewsector; // Render (potentially) multiple views for stereo 3d float viewShift[3]; @@ -848,7 +848,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo clipper.SafeAddClipRangeRealAngles(ViewAngle.BAMs() + a1, ViewAngle.BAMs() - a1); ProcessScene(toscreen); - if (mainview && toscreen) EndDrawScene(retval); // do not call this for camera textures. + if (mainview && toscreen) EndDrawScene(lviewsector); // do not call this for camera textures. if (mainview && FGLRenderBuffers::IsEnabled()) { mBuffers->BlitSceneToTexture(); @@ -860,7 +860,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo // This should be done after postprocessing, not before. mBuffers->BindCurrentFB(); glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); - DrawBlend(viewsector); + DrawBlend(lviewsector); } mDrawingScene2D = false; eye->TearDown(); @@ -869,7 +869,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo gl_frameCount++; // This counter must be increased right before the interpolations are restored. interpolator.RestoreInterpolations (); - return retval; + return lviewsector; } //-----------------------------------------------------------------------------