- groundwork for separating the portal data from the renderer.

With GLPortal being responsible for all the setup a lot of code was tied to the backend.
Now FDrawInfo will manage the setup and only call pure data generation functions in the actual portal object.
This commit is contained in:
Christoph Oelckers 2018-10-24 00:19:07 +02:00
parent 93dac4e4d8
commit 3e4dcbe2b4
7 changed files with 131 additions and 210 deletions

View file

@ -40,6 +40,7 @@
#include "hwrenderer/scene/hw_clipper.h"
#include "gl/scene/gl_portal.h"
#include "gl/renderer/gl_renderstate.h"
#include "gl/data/gl_viewpointbuffer.h"
#include "gl/dynlights/gl_lightbuffer.h"
#include "gl/models/gl_models.h"
@ -311,6 +312,18 @@ void FDrawInfo::DrawHUDModel(HUDSprite *huds, FRenderState &state)
renderer.RenderHUDModel(huds->weapon, huds->mx, huds->my);
}
void FDrawInfo::RenderPortal(IPortal *p, bool usestencil)
{
auto gp = static_cast<GLPortal *>(p);
gp->SetupStencil(this, gl_RenderState, usestencil);
auto new_di = StartDrawInfo(Viewpoint, &VPUniforms);
new_di->mCurrentPortal = gp;
gp->DrawContents(new_di);
new_di->EndDrawInfo();
GLRenderer->mViewpoints->Bind(vpIndex);
gp->RemoveStencil(this, gl_RenderState, usestencil);
}
void FDrawInfo::SetDepthMask(bool on)
{
@ -323,6 +336,11 @@ void FDrawInfo::SetDepthFunc(int func)
glDepthFunc(df2gl[func]);
}
void FDrawInfo::SetDepthRange(float min, float max)
{
glDepthRange(min, max);
}
void FDrawInfo::EnableDrawBufferAttachments(bool on)
{
gl_RenderState.EnableDrawBuffers(on? gl_RenderState.GetPassDrawBufferCount() : 1);
@ -342,6 +360,31 @@ void FDrawInfo::SetStencil(int offs, int op, int flags)
glDisable(GL_DEPTH_TEST);
else
glEnable(GL_DEPTH_TEST);
if (flags & SF_DepthClear)
glClear(GL_DEPTH_BUFFER_BIT);
}
//==========================================================================
//
//
//
//==========================================================================
void FDrawInfo::ClearScreen()
{
bool multi = !!glIsEnabled(GL_MULTISAMPLE);
GLRenderer->mViewpoints->Set2D(SCREENWIDTH, SCREENHEIGHT);
gl_RenderState.SetColor(0, 0, 0);
gl_RenderState.Apply();
glDisable(GL_MULTISAMPLE);
glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4);
glEnable(GL_DEPTH_TEST);
if (multi) glEnable(GL_MULTISAMPLE);
}

View file

@ -47,9 +47,11 @@ struct FDrawInfo : public HWDrawInfo
void DrawIndexed(EDrawType dt, FRenderState &state, int index, int count, bool apply = true) override;
void DrawModel(GLSprite *spr, FRenderState &state) override;
void DrawHUDModel(HUDSprite *spr, FRenderState &state) override;
void RenderPortal(IPortal *p, bool stencil) override;
void SetDepthMask(bool on) override;
void SetDepthFunc(int func) override;
void SetDepthRange(float min, float max) override;
void EnableDrawBufferAttachments(bool on) override;
void SetStencil(int offs, int op, int flags) override;
@ -69,6 +71,7 @@ struct FDrawInfo : public HWDrawInfo
void EndDrawScene(sector_t * viewsector);
void DrawEndScene2D(sector_t * viewsector);
bool SetDepthClamp(bool on) override;
void ClearScreen() override;
static FDrawInfo *StartDrawInfo(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
FDrawInfo *EndDrawInfo();

View file

@ -54,38 +54,14 @@
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
EXTERN_CVAR(Bool, gl_portals)
EXTERN_CVAR(Bool, gl_noquery)
EXTERN_CVAR(Int, r_mirror_recursions)
//==========================================================================
//
//
//
//==========================================================================
void GLPortal::ClearScreen(HWDrawInfo *di)
{
bool multi = !!glIsEnabled(GL_MULTISAMPLE);
GLRenderer->mViewpoints->Set2D(SCREENWIDTH, SCREENHEIGHT);
gl_RenderState.SetColor(0, 0, 0);
gl_RenderState.Apply();
glDisable(GL_MULTISAMPLE);
glDisable(GL_DEPTH_TEST);
glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4);
glEnable(GL_DEPTH_TEST);
if (multi) glEnable(GL_MULTISAMPLE);
}
//-----------------------------------------------------------------------------
//
// DrawPortalStencil
//
//-----------------------------------------------------------------------------
void GLPortal::DrawPortalStencil(int pass)
void GLPortal::DrawPortalStencil(HWDrawInfo *di, FRenderState &state, int pass)
{
if (mPrimIndices.Size() == 0)
{
@ -97,18 +73,17 @@ void GLPortal::DrawPortalStencil(int pass)
mPrimIndices[i * 2 + 1] = lines[i].vertcount;
}
}
gl_RenderState.Apply();
for (unsigned int i = 0; i < mPrimIndices.Size(); i += 2)
{
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_FAN, mPrimIndices[i], mPrimIndices[i + 1]);
di->Draw(DT_TriangleFan, state, mPrimIndices[i], mPrimIndices[i + 1], i==0);
}
if (NeedCap() && lines.Size() > 1)
{
if (pass == STP_AllInOne) glDepthMask(false);
else if (pass == STP_DepthRestore) glDepthRange(1, 1);
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_FAN, FFlatVertexBuffer::STENCILTOP_INDEX, 4);
GLRenderer->mVBO->RenderArray(GL_TRIANGLE_FAN, FFlatVertexBuffer::STENCILBOTTOM_INDEX, 4);
if (pass == STP_DepthRestore) glDepthRange(0, 1);
// The cap's depth handling needs special treatment so that it won't block further portal caps.
if (pass == STP_DepthRestore) di->SetDepthRange(1, 1);
di->Draw(DT_TriangleFan, state, FFlatVertexBuffer::STENCILTOP_INDEX, 4);
di->Draw(DT_TriangleFan, state, FFlatVertexBuffer::STENCILBOTTOM_INDEX, 4);
if (pass == STP_DepthRestore) di->SetDepthRange(0, 1);
}
}
@ -120,58 +95,46 @@ void GLPortal::DrawPortalStencil(int pass)
void GLPortal::SetupStencil(HWDrawInfo *di, FRenderState &state, bool usestencil)
{
Clocker c(PortalAll);
rendered_portals++;
if (usestencil)
{
// Create stencil
glStencilFunc(GL_EQUAL, screen->stencilValue, ~0); // create stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); // increment stencil of valid pixels
glColorMask(0, 0, 0, 0); // don't write to the graphics buffer
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
gl_RenderState.ResetColor();
glDepthFunc(GL_LESS);
gl_RenderState.Apply();
state.SetEffect(EFF_STENCIL);
state.EnableTexture(false);
state.ResetColor();
if (NeedDepthBuffer())
{
glDepthMask(false); // don't write to Z-buffer!
DrawPortalStencil(STP_Stencil);
di->SetStencil(0, SOP_Increment, SF_ColorMaskOff | SF_DepthMaskOff);
di->SetDepthFunc(DF_Less);
DrawPortalStencil(di, state, STP_Stencil);
// Clear Z-buffer
glStencilFunc(GL_EQUAL, screen->stencilValue + 1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
glDepthMask(true); // enable z-buffer again
glDepthRange(1, 1);
glDepthFunc(GL_ALWAYS);
DrawPortalStencil(STP_DepthClear);
di->SetStencil(1, SOP_Keep, SF_ColorMaskOff);
di->SetDepthRange(1, 1);
di->SetDepthFunc(DF_Always);
DrawPortalStencil(di, state, STP_DepthClear);
// set normal drawing mode
gl_RenderState.EnableTexture(true);
glDepthFunc(GL_LESS);
glColorMask(1, 1, 1, 1);
gl_RenderState.SetEffect(EFF_NONE);
glDepthRange(0, 1);
GLuint sampleCount = 1;
state.EnableTexture(true);
di->SetStencil(1, SOP_Keep, SF_AllOn);
di->SetDepthRange(0, 1);
di->SetDepthFunc(DF_Less);
state.SetEffect(EFF_NONE);
}
else
{
// No z-buffer is needed therefore we can skip all the complicated stuff that is involved
// No occlusion queries will be done here. For these portals the overhead is far greater
// than the benefit.
// Note: We must draw the stencil with z-write enabled here because there is no second pass!
di->SetStencil(0, SOP_Increment, SF_ColorMaskOff);
di->SetDepthFunc(DF_Less);
DrawPortalStencil(di, state, STP_AllInOne);
glDepthMask(true);
DrawPortalStencil(STP_AllInOne);
glStencilFunc(GL_EQUAL, screen->stencilValue + 1, ~0); // draw sky into stencil
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); // this stage doesn't modify the stencil
gl_RenderState.EnableTexture(true);
glColorMask(1, 1, 1, 1);
gl_RenderState.SetEffect(EFF_NONE);
glDisable(GL_DEPTH_TEST);
glDepthMask(false); // don't write to Z-buffer!
di->SetStencil(1, SOP_Keep, SF_DepthTestOff | SF_DepthMaskOff);
state.EnableTexture(true);
state.SetEffect(EFF_NONE);
}
screen->stencilValue++;
@ -180,139 +143,66 @@ void GLPortal::SetupStencil(HWDrawInfo *di, FRenderState &state, bool usestencil
{
if (!NeedDepthBuffer())
{
glDepthMask(false);
glDisable(GL_DEPTH_TEST);
di->SetStencil(0, SOP_Keep, SF_DepthTestOff | SF_DepthMaskOff);
}
}
// save viewpoint
savedvisibility = di->Viewpoint.camera ? di->Viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0);
}
void GLPortal::RemoveStencil(HWDrawInfo *di, FRenderState &state, bool usestencil)
{
Clocker c(PortalAll);
bool needdepth = NeedDepthBuffer();
// Restore the old view
auto &vp = di->Viewpoint;
if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility;
if (usestencil)
{
glColorMask(0, 0, 0, 0); // no graphics
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.ResetColor();
gl_RenderState.EnableTexture(false);
gl_RenderState.Apply();
state.SetEffect(EFF_NONE);
state.ResetColor();
state.EnableTexture(false);
if (needdepth)
{
// first step: reset the depth buffer to max. depth
glDepthRange(1, 1); // always
glDepthFunc(GL_ALWAYS); // write the farthest depth value
DrawPortalStencil(STP_DepthClear);
}
else
{
glEnable(GL_DEPTH_TEST);
di->SetStencil(0, SOP_Keep, SF_ColorMaskOff);
di->SetDepthRange(1, 1); // always
di->SetDepthFunc(DF_Always); // write the farthest depth value
DrawPortalStencil(di, state, STP_DepthClear);
}
// second step: restore the depth buffer to the previous values and reset the stencil
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
glStencilFunc(GL_EQUAL, screen->stencilValue, ~0); // draw sky into stencil
DrawPortalStencil(STP_DepthRestore);
glDepthFunc(GL_LESS);
di->SetStencil(0, SOP_Decrement, SF_ColorMaskOff);
di->SetDepthRange(0, 1);
di->SetDepthFunc(DF_LEqual);
DrawPortalStencil(di, state, STP_DepthRestore);
gl_RenderState.EnableTexture(true);
gl_RenderState.SetEffect(EFF_NONE);
glColorMask(1, 1, 1, 1);
state.EnableTexture(true);
state.SetEffect(EFF_NONE);
screen->stencilValue--;
// restore old stencil op.
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_EQUAL, screen->stencilValue, ~0); // draw sky into stencil
}
else
{
if (needdepth)
{
glClear(GL_DEPTH_BUFFER_BIT);
}
else
{
glEnable(GL_DEPTH_TEST);
glDepthMask(true);
}
auto &vp = di->Viewpoint;
state.ResetColor();
state.SetEffect(EFF_STENCIL);
state.EnableTexture(false);
state.SetRenderStyle(STYLE_Source);
// Restore the old view
if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility;
di->SetStencil(0, SOP_Keep, needdepth? SF_ColorMaskOff | SF_DepthClear : SF_ColorMaskOff);
di->SetDepthRange(0, 1);
di->SetDepthFunc(DF_LEqual);
DrawPortalStencil(di, state, STP_DepthRestore);
// This draws a valid z-buffer into the stencil's contents to ensure it
// doesn't get overwritten by the level's geometry.
gl_RenderState.ResetColor();
glDepthFunc(GL_LEQUAL);
glDepthRange(0, 1);
glColorMask(0, 0, 0, 1); // mark portal in alpha channel but don't touch color
gl_RenderState.SetEffect(EFF_STENCIL);
gl_RenderState.EnableTexture(false);
gl_RenderState.SetRenderStyle(STYLE_Source);
gl_RenderState.Apply();
DrawPortalStencil(STP_DepthRestore);
gl_RenderState.SetEffect(EFF_NONE);
gl_RenderState.EnableTexture(true);
glColorMask(1, 1, 1, 1); // mark portal in alpha channel but don't touch color
glDepthFunc(GL_LESS);
state.SetEffect(EFF_NONE);
state.EnableTexture(true);
}
di->SetStencil(0, SOP_Keep, SF_AllOn);
}
//-----------------------------------------------------------------------------
//
// Start
//
//-----------------------------------------------------------------------------
bool GLPortal::Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi)
{
*pDi = nullptr;
rendered_portals++;
Clocker c(PortalAll);
if (!gl_portals)
{
return false;
}
SetupStencil(outer_di, gl_RenderState, usestencil);
*pDi = FDrawInfo::StartDrawInfo(outer_di->Viewpoint, &outer_di->VPUniforms);
(*pDi)->mCurrentPortal = this;
// save viewpoint
savedvisibility = outer_di->Viewpoint.camera ? outer_di->Viewpoint.camera->renderflags & RF_MAYBEINVISIBLE : ActorRenderFlags::FromInt(0);
return true;
}
//-----------------------------------------------------------------------------
//
// End
//
//-----------------------------------------------------------------------------
void GLPortal::End(HWDrawInfo *di, bool usestencil)
{
Clocker c(PortalAll);
di = static_cast<FDrawInfo*>(di)->EndDrawInfo();
GLRenderer->mViewpoints->Bind(static_cast<FDrawInfo*>(di)->vpIndex);
auto &vp = di->Viewpoint;
// Restore the old view
if (vp.camera != nullptr) vp.camera->renderflags = (vp.camera->renderflags & ~RF_MAYBEINVISIBLE) | savedvisibility;
RemoveStencil(di, gl_RenderState, usestencil);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//
@ -402,7 +292,7 @@ void GLHorizonPortal::DrawContents(HWDrawInfo *hwdi)
gltexture=FMaterial::ValidateTexture(sp->texture, false, true);
if (!gltexture)
{
ClearScreen(di);
di->ClearScreen();
return;
}
di->SetCameraPos(vp.Pos);

View file

@ -58,11 +58,12 @@ private:
STP_DepthRestore,
STP_AllInOne
};
void DrawPortalStencil(int pass);
void DrawPortalStencil(HWDrawInfo *di, FRenderState &state, int pass);
ActorRenderFlags savedvisibility;
TArray<unsigned int> mPrimIndices;
public:
void SetupStencil(HWDrawInfo *di, FRenderState &state, bool usestencil);
void RemoveStencil(HWDrawInfo *di, FRenderState &state, bool usestencil);
@ -70,10 +71,6 @@ protected:
int level;
GLPortal(FPortalSceneState *state, bool local = false) : IPortal(state, local) { }
bool Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi) override;
void End(HWDrawInfo *di, bool usestencil) override;
void ClearScreen(HWDrawInfo *di);
};
class GLScenePortal : public GLPortal
@ -98,7 +95,7 @@ public:
static_cast<FDrawInfo*>(di)->DrawScene(DM_PORTAL);
mScene->Shutdown(di);
}
else ClearScreen(di);
else di->ClearScreen();
}
virtual void RenderAttached(HWDrawInfo *di) { return mScene->RenderAttached(di); }
};

View file

@ -29,7 +29,8 @@ enum EStencilFlags
SF_AllOn = 0,
SF_ColorMaskOff = 1,
SF_DepthMaskOff = 2,
SF_DepthTestOff = 4
SF_DepthTestOff = 4,
SF_DepthClear = 8
};
enum EStencilOp
@ -332,16 +333,18 @@ public:
virtual std::pair<FFlatVertex *, unsigned int> AllocVertices(unsigned int count) = 0;
virtual void ClearScreen() = 0;
virtual void Draw(EDrawType dt, FRenderState &state, int index, int count, bool apply = true) = 0;
virtual void DrawIndexed(EDrawType dt, FRenderState &state, int index, int count, bool apply = true) = 0;
virtual void DrawModel(GLSprite *spr, FRenderState &state) = 0;
virtual void DrawHUDModel(HUDSprite *spr, FRenderState &state) = 0;
virtual void RenderPortal(IPortal *p, bool usestencil) = 0;
// Immediate render state change commands. These only change infrequently and should not clutter the render state.
virtual void SetDepthMask(bool on) = 0;
virtual void SetDepthFunc(int func) = 0;
virtual void SetDepthRange(float min, float max) = 0;
virtual void EnableDrawBufferAttachments(bool on) = 0;
virtual void SetStencil(int offs, int op, int flags) = 0;

View file

@ -34,6 +34,7 @@
#include "g_levellocals.h"
EXTERN_CVAR(Int, r_mirror_recursions)
EXTERN_CVAR(Bool, gl_portals)
//-----------------------------------------------------------------------------
//
@ -82,20 +83,15 @@ void FPortalSceneState::EndFrame(HWDrawInfo *di)
indent += " ";
}
// Only use occlusion query if there are more than 2 portals.
// Otherwise there's too much overhead.
// (And don't forget to consider the separating null pointers!)
bool usequery = di->Portals.Size() > 2 + (unsigned)renderdepth;
while (di->Portals.Pop(p) && p)
{
if (gl_portalinfo)
{
Printf("%sProcessing %s, depth = %d, query = %d\n", indent.GetChars(), p->GetName(), renderdepth, usequery);
Printf("%sProcessing %s, depth = %d\n", indent.GetChars(), p->GetName(), renderdepth);
}
if (p->lines.Size() > 0)
{
p->RenderPortal(true, usequery, di);
RenderPortal(p, true, di);
}
delete p;
}
@ -146,13 +142,20 @@ bool FPortalSceneState::RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di
if (best)
{
portals.Delete(bestindex);
best->RenderPortal(false, false, outer_di);
RenderPortal(best, false, outer_di);
delete best;
return true;
}
return false;
}
void FPortalSceneState::RenderPortal(IPortal *p, bool usestencil, HWDrawInfo *outer_di)
{
if (gl_portals) outer_di->RenderPortal(p, usestencil);
}
//-----------------------------------------------------------------------------
//
//

View file

@ -53,27 +53,12 @@ public:
virtual bool NeedDepthBuffer() { return true; }
virtual void DrawContents(HWDrawInfo *di) = 0;
virtual void RenderAttached(HWDrawInfo *di) {}
virtual bool Start(bool usestencil, bool doquery, HWDrawInfo *outer_di, HWDrawInfo **pDi) = 0;
virtual void End(HWDrawInfo *di, bool usestencil) = 0;
void AddLine(GLWall * l)
{
lines.Push(*l);
}
void RenderPortal(bool usestencil, bool doquery, HWDrawInfo *outer_di)
{
// Start may perform an occlusion query. If that returns 0 there
// is no need to draw the stencil's contents and there's also no
// need to restore the affected area becasue there is none!
HWDrawInfo *di;
if (Start(usestencil, doquery, outer_di, &di))
{
DrawContents(di);
End(di, usestencil);
}
}
};
@ -107,8 +92,7 @@ struct FPortalSceneState
void StartFrame();
bool RenderFirstSkyPortal(int recursion, HWDrawInfo *outer_di);
void EndFrame(HWDrawInfo *outer_di);
void RenderPortal(IPortal *p, bool usestencil, HWDrawInfo *outer_di);
};
inline IPortal::IPortal(FPortalSceneState *s, bool local) : mState(s)
@ -117,8 +101,6 @@ inline IPortal::IPortal(FPortalSceneState *s, bool local) : mState(s)
}
class HWScenePortalBase
{
protected: