From 9a1603b2467548cf3fe448dd9c41c5f84a7cc526 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 26 Apr 2018 19:25:11 +0200 Subject: [PATCH] - made GLWall ready for multithreaded processing. * to do this efficiently the amount of required vertices needs to be calculated up-front * always create the vertices in the data generation pass, not the render pass. * added synchronisation code to the vertex buffer allocator. Without multithreading this causes a slight slowdown, due to added processing cost. (Frozen Time bridge scene drops from 47 fps to 44 fps on my test machine. --- src/gl/data/gl_vertexbuffer.h | 24 +++++++++++--- src/gl/scene/gl_drawinfo.cpp | 7 ++-- src/gl/scene/gl_portal.cpp | 1 - src/gl/scene/gl_vertex.cpp | 60 +++++++++++++++++++++++++++++----- src/gl/scene/gl_wall.h | 2 ++ src/gl/scene/gl_walls.cpp | 6 ++-- src/gl/scene/gl_walls_draw.cpp | 26 ++++----------- 7 files changed, 84 insertions(+), 42 deletions(-) diff --git a/src/gl/data/gl_vertexbuffer.h b/src/gl/data/gl_vertexbuffer.h index 321714ad9..7b5a28416 100644 --- a/src/gl/data/gl_vertexbuffer.h +++ b/src/gl/data/gl_vertexbuffer.h @@ -23,6 +23,9 @@ #ifndef __VERTEXBUFFER_H #define __VERTEXBUFFER_H +#include +#include +#include #include "tarray.h" #include "hwrenderer/utility/hw_clock.h" #include "gl/system/gl_interface.h" @@ -93,7 +96,8 @@ class FFlatVertexBuffer : public FVertexBuffer, public FFlatVertexGenerator { FFlatVertex *map; unsigned int mIndex; - unsigned int mCurIndex; + std::atomic mCurIndex; + std::mutex mBufferMutex; unsigned int mNumReserved; @@ -125,12 +129,22 @@ public: { return &map[mCurIndex]; } - FFlatVertex *Alloc(int num, int *poffset) + + template + FFlatVertex *Alloc(int num, T *poffset) { + again: FFlatVertex *p = GetBuffer(); - *poffset = mCurIndex; - mCurIndex += num; - if (mCurIndex >= BUFFER_SIZE_TO_USE) mCurIndex = mIndex; + auto index = mCurIndex.fetch_add(num); + *poffset = static_cast(index); + if (index + num >= BUFFER_SIZE_TO_USE) + { + std::lock_guard lock(mBufferMutex); + if (mCurIndex >= BUFFER_SIZE_TO_USE) // retest condition, in case another thread got here first + mCurIndex = mIndex; + + if (index >= BUFFER_SIZE_TO_USE) goto again; + } return p; } diff --git a/src/gl/scene/gl_drawinfo.cpp b/src/gl/scene/gl_drawinfo.cpp index f2f49e60c..73619e924 100644 --- a/src/gl/scene/gl_drawinfo.cpp +++ b/src/gl/scene/gl_drawinfo.cpp @@ -462,11 +462,8 @@ void GLDrawList::SortWallIntoWall(SortNode * head,SortNode * sort) w->ztop[0]=ws->ztop[1]=izt; w->zbottom[0]=ws->zbottom[1]=izb; w->tcs[GLWall::LOLFT].u = w->tcs[GLWall::UPLFT].u = ws->tcs[GLWall::LORGT].u = ws->tcs[GLWall::UPRGT].u = iu; - if (gl.buffermethod == BM_DEFERRED) - { - ws->MakeVertices(false); - w->MakeVertices(false); - } + ws->MakeVertices(false); + w->MakeVertices(false); SortNode * sort2=SortNodes.GetNew(); memset(sort2,0,sizeof(SortNode)); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 9b09697ed..d023d365a 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -134,7 +134,6 @@ void GLPortal::DrawPortalStencil() for (unsigned int i = 0; i < lines.Size(); i++) { - if (gl.buffermethod != BM_DEFERRED) lines[i].MakeVertices(false); mPrimIndices[i * 2] = lines[i].vertindex; mPrimIndices[i * 2 + 1] = lines[i].vertcount; } diff --git a/src/gl/scene/gl_vertex.cpp b/src/gl/scene/gl_vertex.cpp index e9aef4459..d0e59aa3e 100644 --- a/src/gl/scene/gl_vertex.cpp +++ b/src/gl/scene/gl_vertex.cpp @@ -35,8 +35,6 @@ EXTERN_CVAR(Bool, gl_seamless) void GLWall::SplitUpperEdge(FFlatVertex *&ptr) { - if (seg == NULL || seg->sidedef == NULL || (seg->sidedef->Flags & WALLF_POLYOBJ) || seg->sidedef->numsegs == 1) return; - side_t *sidedef = seg->sidedef; float polyw = glseg.fracright - glseg.fracleft; float facu = (tcs[UPRGT].u - tcs[UPLFT].u) / polyw; @@ -71,8 +69,6 @@ void GLWall::SplitUpperEdge(FFlatVertex *&ptr) void GLWall::SplitLowerEdge(FFlatVertex *&ptr) { - if (seg == NULL || seg->sidedef == NULL || (seg->sidedef->Flags & WALLF_POLYOBJ) || seg->sidedef->numsegs == 1) return; - side_t *sidedef = seg->sidedef; float polyw = glseg.fracright - glseg.fracleft; float facu = (tcs[LORGT].u - tcs[LOLFT].u) / polyw; @@ -173,20 +169,68 @@ void GLWall::SplitRightEdge(FFlatVertex *&ptr) // //========================================================================== -void GLWall::CreateVertices(FFlatVertex *&ptr, bool nosplit) +void GLWall::CreateVertices(FFlatVertex *&ptr, bool split) { - bool split = (gl_seamless && !nosplit && seg->sidedef != nullptr && !(seg->sidedef->Flags & WALLF_POLYOBJ) && !(flags & GLWF_NOSPLIT)); ptr->Set(glseg.x1, zbottom[0], glseg.y1, tcs[LOLFT].u, tcs[LOLFT].v); ptr++; if (split && glseg.fracleft == 0) SplitLeftEdge(ptr); ptr->Set(glseg.x1, ztop[0], glseg.y1, tcs[UPLFT].u, tcs[UPLFT].v); ptr++; - if (split && !(flags & GLWF_NOSPLITUPPER)) SplitUpperEdge(ptr); + if (split && !(flags & GLWF_NOSPLITUPPER && seg->sidedef->numsegs > 1)) SplitUpperEdge(ptr); ptr->Set(glseg.x2, ztop[1], glseg.y2, tcs[UPRGT].u, tcs[UPRGT].v); ptr++; if (split && glseg.fracright == 1) SplitRightEdge(ptr); ptr->Set(glseg.x2, zbottom[1], glseg.y2, tcs[LORGT].u, tcs[LORGT].v); ptr++; - if (split && !(flags & GLWF_NOSPLITLOWER)) SplitLowerEdge(ptr); + if (split && !(flags & GLWF_NOSPLITLOWER) && seg->sidedef->numsegs > 1) SplitLowerEdge(ptr); } +//========================================================================== +// +// +// +//========================================================================== + +int GLWall::CountVertices() +{ + int cnt = 4; + vertex_t * vi = vertexes[0]; + if (glseg.fracleft == 0 && vi != nullptr && vi->numheights) + { + int i = 0; + + while (inumheights && vi->heightlist[i] <= zbottom[0]) i++; + while (inumheights && vi->heightlist[i] < ztop[0]) + { + i++; + cnt++; + } + } + auto sidedef = seg->sidedef; + if (!(flags & (GLWF_NOSPLITLOWER | GLWF_NOSPLITUPPER)) && sidedef->numsegs > 1) + { + int cnt2 = 0; + for (int i = sidedef->numsegs - 2; i >= 0; i--) + { + float sidefrac = sidedef->segs[i]->sidefrac; + if (sidefrac >= glseg.fracright) continue; + if (sidefrac <= glseg.fracleft) break; + cnt2++; + } + if ((flags & (GLWF_NOSPLITLOWER | GLWF_NOSPLITUPPER)) == (GLWF_NOSPLITLOWER | GLWF_NOSPLITUPPER)) cnt2 <<= 1; + cnt += cnt2; + } + vi = vertexes[1]; + if (glseg.fracright == 1 && vi != nullptr && vi->numheights) + { + int i = 0; + + while (inumheights && vi->heightlist[i] <= zbottom[1]) i++; + while (inumheights && vi->heightlist[i] < ztop[1]) + { + i++; + cnt++; + } + } + return cnt; +} \ No newline at end of file diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h index 9b112963d..f94889615 100644 --- a/src/gl/scene/gl_wall.h +++ b/src/gl/scene/gl_wall.h @@ -266,6 +266,8 @@ private: void SplitUpperEdge(FFlatVertex *&ptr); void SplitLowerEdge(FFlatVertex *&ptr); + int CountVertices(); + public: GLWall(GLSceneDrawer *drawer) diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp index b02165bc2..35351d454 100644 --- a/src/gl/scene/gl_walls.cpp +++ b/src/gl/scene/gl_walls.cpp @@ -50,7 +50,7 @@ void FDrawInfo::AddWall(GLWall *wall) if (translucent) // translucent walls { wall->ViewDistance = (r_viewpoint.Pos - (wall->seg->linedef->v1->fPos() + wall->seg->linedef->Delta() / 2)).XY().LengthSquared(); - if (gl.buffermethod == BM_DEFERRED) wall->MakeVertices(true); + wall->MakeVertices(true); auto newwall = drawlists[GLDL_TRANSLUCENT].NewWall(); *newwall = *wall; } @@ -73,7 +73,7 @@ void FDrawInfo::AddWall(GLWall *wall) { list = masked ? GLDL_MASKEDWALLS : GLDL_PLAINWALLS; } - if (gl.buffermethod == BM_DEFERRED) wall->MakeVertices(false); + wall->MakeVertices(false); auto newwall = drawlists[list].NewWall(); *newwall = *wall; } @@ -133,7 +133,7 @@ void GLWall::PutPortal(HWDrawInfo *di, int ptype) { GLPortal * portal; - if (gl.buffermethod == BM_DEFERRED) MakeVertices(false); + MakeVertices(false); switch (ptype) { // portals don't go into the draw list. diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 347a07d04..9ace4fdcf 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -154,9 +154,11 @@ void GLWall::MakeVertices(bool nosplit) { if (vertcount == 0) { - FFlatVertex *ptr = GLRenderer->mVBO->GetBuffer(); - CreateVertices(ptr, nosplit); - vertcount = GLRenderer->mVBO->GetCount(ptr, &vertindex); + bool split = (gl_seamless && !nosplit && seg->sidedef != nullptr && !(seg->sidedef->Flags & WALLF_POLYOBJ) && !(flags & GLWF_NOSPLIT)); + vertcount = split ? CountVertices() : 4; + + FFlatVertex *ptr = GLRenderer->mVBO->Alloc(vertcount, &vertindex); + CreateVertices(ptr, split); } } @@ -170,25 +172,9 @@ void GLWall::MakeVertices(bool nosplit) void GLWall::RenderWall(int textured) { + assert(vertcount > 0); gl_RenderState.Apply(); gl_RenderState.ApplyLightIndex(dynlightindex); - if (gl.buffermethod != BM_DEFERRED) - { - MakeVertices(!!(textured&RWF_NOSPLIT)); - } - else if (vertcount == 0) - { - // 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); - qd.Set(2, glseg.x2, ztop[1], glseg.y2, tcs[UPRGT].u, tcs[UPRGT].v); - qd.Set(3, glseg.x2, zbottom[1], glseg.y2, tcs[LORGT].u, tcs[LORGT].v); - qd.Render(GL_TRIANGLE_FAN); - vertexcount += 4; - return; - } GLRenderer->mVBO->RenderArray(GL_TRIANGLE_FAN, vertindex, vertcount); vertexcount += vertcount; }