- 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.
This commit is contained in:
Christoph Oelckers 2018-04-26 19:25:11 +02:00
parent 3111ec97bb
commit 9a1603b246
7 changed files with 84 additions and 42 deletions

View file

@ -23,6 +23,9 @@
#ifndef __VERTEXBUFFER_H
#define __VERTEXBUFFER_H
#include <atomic>
#include <thread>
#include <mutex>
#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<unsigned int> mCurIndex;
std::mutex mBufferMutex;
unsigned int mNumReserved;
@ -125,12 +129,22 @@ public:
{
return &map[mCurIndex];
}
FFlatVertex *Alloc(int num, int *poffset)
template<class T>
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<T>(index);
if (index + num >= BUFFER_SIZE_TO_USE)
{
std::lock_guard<std::mutex> 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;
}

View file

@ -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));

View file

@ -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;
}

View file

@ -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 (i<vi->numheights && vi->heightlist[i] <= zbottom[0]) i++;
while (i<vi->numheights && 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 (i<vi->numheights && vi->heightlist[i] <= zbottom[1]) i++;
while (i<vi->numheights && vi->heightlist[i] < ztop[1])
{
i++;
cnt++;
}
}
return cnt;
}

View file

@ -266,6 +266,8 @@ private:
void SplitUpperEdge(FFlatVertex *&ptr);
void SplitLowerEdge(FFlatVertex *&ptr);
int CountVertices();
public:
GLWall(GLSceneDrawer *drawer)

View file

@ -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.

View file

@ -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;
}