- allow reallocation of light buffer if more lights are needed.

- added a light preprocessing pass to the renderer so that a non-persistent buffer can be used with minimal mapping/unmapping. This only gets used if necessary because it adds some overhead to the renderer.
This commit is contained in:
Christoph Oelckers 2014-08-19 14:18:21 +02:00
parent 38796e7714
commit 00d7707aef
11 changed files with 323 additions and 169 deletions

View File

@ -45,15 +45,20 @@
#include "gl/system/gl_interface.h"
#include "gl/utility//gl_clock.h"
static const int BUFFER_SIZE = 160000; // This means 80000 lights per frame and 160000*16 bytes == 2.56 MB.
static const int INITIAL_BUFFER_SIZE = 160000; // This means 80000 lights per frame and 160000*16 bytes == 2.56 MB.
float *mMap;
FLightBuffer::FLightBuffer()
{
mBufferSize = INITIAL_BUFFER_SIZE;
mByteSize = mBufferSize * sizeof(float);
if (gl.flags & RFL_SHADER_STORAGE_BUFFER)
{
mBufferType = GL_SHADER_STORAGE_BUFFER;
mBlockAlign = -1;
mBlockSize = BUFFER_SIZE;
mBlockSize = mBufferSize;
}
else
{
@ -64,12 +69,17 @@ FLightBuffer::FLightBuffer()
}
glGenBuffers(1, &mBufferId);
glBindBuffer(mBufferType, mBufferId);
unsigned int bytesize = BUFFER_SIZE * 4 * sizeof(float);
glBufferStorage(mBufferType, bytesize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
void *map = glMapBufferRange(mBufferType, 0, bytesize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (float*)map;
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, mBufferId);
if (gl.flags & RFL_SHADER_STORAGE_BUFFER)
{
glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
}
else
{
glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
mBufferPointer = NULL;
}
Clear();
mLastMappedIndex = UINT_MAX;
@ -84,7 +94,8 @@ FLightBuffer::~FLightBuffer()
void FLightBuffer::Clear()
{
mIndex = 0;
mBufferArray.Clear();
mIndices.Clear();
mUploadIndex = 0;
}
int FLightBuffer::UploadLights(FDynLightData &data)
@ -120,13 +131,45 @@ int FLightBuffer::UploadLights(FDynLightData &data)
if (totalsize <= 1) return -1;
if (mIndex + totalsize > BUFFER_SIZE)
if (mIndex + totalsize > mBufferSize)
{
return -1; // we ran out of space. All following lights will be ignored
// reallocate the buffer with twice the size
unsigned int newbuffer;
// first unmap the old buffer
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
// create and bind the new buffer, bind the old one to a copy target (too bad that DSA is not yet supported well enough to omit this crap.)
glGenBuffers(1, &newbuffer);
glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, newbuffer);
glBindBuffer(GL_COPY_READ_BUFFER, mBufferId);
// create the new buffer's storage (twice as large as the old one)
mBufferSize *= 2;
mByteSize *= 2;
if (gl.flags & RFL_SHADER_STORAGE_BUFFER)
{
glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT);
}
else
{
glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT);
}
// copy contents and delete the old buffer.
glCopyBufferSubData(GL_COPY_READ_BUFFER, mBufferType, 0, 0, mByteSize/2);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glDeleteBuffers(1, &mBufferId);
mBufferId = newbuffer;
}
float *copyptr;
assert(mBufferPointer != NULL);
if (mBufferPointer == NULL) return -1;
copyptr = mBufferPointer + mIndex * 4;
float parmcnt[] = { 0, size0, size0 + size1, size0 + size1 + size2 };
@ -142,9 +185,23 @@ int FLightBuffer::UploadLights(FDynLightData &data)
return bufferindex;
}
void FLightBuffer::Begin()
{
if (!(gl.flags & RFL_SHADER_STORAGE_BUFFER))
{
glBindBuffer(mBufferType, mBufferId);
mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT);
}
}
void FLightBuffer::Finish()
{
Clear();
if (!(gl.flags & RFL_SHADER_STORAGE_BUFFER))
{
glBindBuffer(mBufferType, mBufferId);
glUnmapBuffer(mBufferType);
mBufferPointer = NULL;
}
}
int FLightBuffer::BindUBO(unsigned int index)

View File

@ -6,15 +6,18 @@ struct FDynLightData;
class FLightBuffer
{
TArray<float> mBufferArray;
TArray<int> mIndices;
unsigned int mBufferId;
float * mBufferPointer;
unsigned int mBufferType;
unsigned int mIndex;
unsigned int mUploadIndex;
unsigned int mLastMappedIndex;
unsigned int mBlockAlign;
unsigned int mBlockSize;
unsigned int mBufferSize;
unsigned int mByteSize;
public:
@ -22,10 +25,14 @@ public:
~FLightBuffer();
void Clear();
int UploadLights(FDynLightData &data);
void Begin();
void Finish();
int BindUBO(unsigned int index);
unsigned int GetBlockSize() const { return mBlockSize; }
unsigned int GetBufferType() const { return mBufferType; }
unsigned int GetIndexPtr() const { return mIndices.Size(); }
void StoreIndex(int index) { mIndices.Push(index); }
int GetIndex(int i) const { return mIndices[i]; }
};
#endif

View File

@ -110,7 +110,7 @@ void FGLRenderer::Initialize()
mVBO = new FFlatVertexBuffer;
mSkyVBO = new FSkyVertexBuffer;
mModelVBO = new FModelVertexBuffer;
mLights = new FLightBuffer;
mLights = new FLightBuffer();
gl_RenderState.SetVertexBuffer(mVBO);
mFBID = 0;
SetupLevel();

View File

@ -692,7 +692,7 @@ SortNode * GLDrawList::DoSort(SortNode * head)
//
//
//==========================================================================
void GLDrawList::DoDraw(int pass, int i)
void GLDrawList::DoDraw(int pass, int i, bool trans)
{
switch(drawitems[i].rendertype)
{
@ -700,7 +700,7 @@ void GLDrawList::DoDraw(int pass, int i)
{
GLFlat * f=&flats[drawitems[i].index];
RenderFlat.Clock();
f->Draw(pass);
f->Draw(pass, trans);
RenderFlat.Unclock();
}
break;
@ -739,13 +739,13 @@ void GLDrawList::DoDrawSorted(SortNode * head)
{
DoDrawSorted(head->left);
}
DoDraw(GLPASS_TRANSLUCENT, head->itemindex);
DoDraw(GLPASS_TRANSLUCENT, head->itemindex, true);
if (head->equal)
{
SortNode * ehead=head->equal;
while (ehead)
{
DoDraw(GLPASS_TRANSLUCENT, ehead->itemindex);
DoDraw(GLPASS_TRANSLUCENT, ehead->itemindex, true);
ehead=ehead->equal;
}
}
@ -779,7 +779,7 @@ void GLDrawList::Draw(int pass)
{
for(unsigned i=0;i<drawitems.Size();i++)
{
DoDraw(pass, i);
DoDraw(pass, i, false);
}
}

View File

@ -26,8 +26,9 @@ enum DrawListType
enum Drawpasses
{
GLPASS_PLAIN, // Main pass without dynamic lights
GLPASS_ALL, // Main pass with dynamic lights
GLPASS_LIGHTSONLY, // only collect dynamic lights
GLPASS_PLAIN, // Main pass without dynamic lights
GLPASS_DECALS, // Draws a decal
GLPASS_DECALS_NOFOG,// Draws a decal without setting the fog (used for passes that need a fog layer)
GLPASS_TRANSLUCENT, // Draws translucent objects
@ -120,7 +121,7 @@ public:
SortNode * SortSpriteList(SortNode * head);
SortNode * DoSort(SortNode * head);
void DoDraw(int pass, int index);
void DoDraw(int pass, int index, bool trans);
void DoDrawSorted(SortNode * node);
void DrawSorted();
void Draw(int pass);

View File

@ -112,10 +112,17 @@ void gl_SetPlaneTextureRotation(const GLSectorPlane * secplane, FMaterial * glte
//==========================================================================
extern FDynLightData lightdata;
bool GLFlat::SetupSubsectorLights(bool lightsapplied, subsector_t * sub)
void GLFlat::SetupSubsectorLights(int pass, subsector_t * sub, int *dli)
{
Plane p;
if (dli != NULL && *dli != -1)
{
gl_RenderState.ApplyLightIndex(GLRenderer->mLights->GetIndex(*dli));
(*dli)++;
return;
}
lightdata.Clear();
FLightNode * node = sub->lighthead;
while (node)
@ -143,9 +150,15 @@ bool GLFlat::SetupSubsectorLights(bool lightsapplied, subsector_t * sub)
node = node->nextLight;
}
dynlightindex = GLRenderer->mLights->UploadLights(lightdata);
gl_RenderState.ApplyLightIndex(dynlightindex);
return false;
int d = GLRenderer->mLights->UploadLights(lightdata);
if (pass == GLPASS_LIGHTSONLY)
{
GLRenderer->mLights->StoreIndex(d);
}
else
{
gl_RenderState.ApplyLightIndex(d);
}
}
//==========================================================================
@ -197,15 +210,59 @@ void GLFlat::DrawSubsector(subsector_t * sub)
//
//==========================================================================
void GLFlat::DrawSubsectors(int pass, bool istrans)
void GLFlat::ProcessLights(bool istrans)
{
bool lightsapplied = false;
dynlightindex = GLRenderer->mLights->GetIndexPtr();
if (sub)
{
// This represents a single subsector
SetupSubsectorLights(GLPASS_LIGHTSONLY, sub);
}
else
{
// Draw the subsectors belonging to this sector
for (int i=0; i<sector->subsectorcount; i++)
{
subsector_t * sub = sector->subsectors[i];
if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags || istrans)
{
SetupSubsectorLights(GLPASS_LIGHTSONLY, sub);
}
}
// Draw the subsectors assigned to it due to missing textures
if (!(renderflags&SSRF_RENDER3DPLANES))
{
gl_subsectorrendernode * node = (renderflags&SSRF_RENDERFLOOR)?
gl_drawinfo->GetOtherFloorPlanes(sector->sectornum) :
gl_drawinfo->GetOtherCeilingPlanes(sector->sectornum);
while (node)
{
SetupSubsectorLights(GLPASS_LIGHTSONLY, node->sub);
node = node->next;
}
}
}
}
//==========================================================================
//
//
//
//==========================================================================
void GLFlat::DrawSubsectors(int pass, bool processlights, bool istrans)
{
int dli = dynlightindex;
gl_RenderState.Apply();
if (sub)
{
// This represents a single subsector
if (pass == GLPASS_ALL) lightsapplied = SetupSubsectorLights(lightsapplied, sub);
if (processlights) SetupSubsectorLights(GLPASS_ALL, sub, &dli);
DrawSubsector(sub);
}
else
@ -216,10 +273,10 @@ void GLFlat::DrawSubsectors(int pass, bool istrans)
for (int i=0; i<sector->subsectorcount; i++)
{
subsector_t * sub = sector->subsectors[i];
// This is just a quick hack to make translucent 3D floors and portals work.
if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags || istrans)
{
if (pass == GLPASS_ALL) lightsapplied = SetupSubsectorLights(lightsapplied, sub);
if (processlights) SetupSubsectorLights(GLPASS_ALL, sub, &dli);
drawcalls.Clock();
glDrawArrays(GL_TRIANGLE_FAN, index, sub->numlines);
drawcalls.Unclock();
@ -237,7 +294,7 @@ void GLFlat::DrawSubsectors(int pass, bool istrans)
subsector_t * sub = sector->subsectors[i];
if (gl_drawinfo->ss_renderflags[sub-subsectors]&renderflags || istrans)
{
if (pass == GLPASS_ALL) lightsapplied = SetupSubsectorLights(lightsapplied, sub);
if (processlights) SetupSubsectorLights(GLPASS_ALL, sub, &dli);
DrawSubsector(sub);
}
}
@ -252,7 +309,7 @@ void GLFlat::DrawSubsectors(int pass, bool istrans)
while (node)
{
if (pass == GLPASS_ALL) lightsapplied = SetupSubsectorLights(lightsapplied, node->sub);
if (processlights) SetupSubsectorLights(GLPASS_ALL, node->sub, &dli);
DrawSubsector(node->sub);
node = node->next;
}
@ -266,7 +323,7 @@ void GLFlat::DrawSubsectors(int pass, bool istrans)
//
//
//==========================================================================
void GLFlat::Draw(int pass)
void GLFlat::Draw(int pass, bool trans) // trans only has meaning for GLPASS_LIGHTSONLY
{
int rel = getExtraLight();
@ -286,10 +343,17 @@ void GLFlat::Draw(int pass)
gl_SetFog(lightlevel, rel, &Colormap, false);
gltexture->Bind();
gl_SetPlaneTextureRotation(&plane, gltexture);
DrawSubsectors(pass, false);
DrawSubsectors(pass, (pass == GLPASS_ALL || dynlightindex > -1), false);
gl_RenderState.EnableTextureMatrix(false);
break;
case GLPASS_LIGHTSONLY:
if (!trans || gltexture)
{
ProcessLights(trans);
}
break;
case GLPASS_TRANSLUCENT:
if (renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE);
gl_SetColor(lightlevel, rel, Colormap, alpha);
@ -298,14 +362,14 @@ void GLFlat::Draw(int pass)
if (!gltexture)
{
gl_RenderState.EnableTexture(false);
DrawSubsectors(pass, true);
DrawSubsectors(pass, false, true);
gl_RenderState.EnableTexture(true);
}
else
{
gltexture->Bind();
gl_SetPlaneTextureRotation(&plane, gltexture);
DrawSubsectors(pass, true);
DrawSubsectors(pass, true, true);
gl_RenderState.EnableTextureMatrix(false);
}
if (renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

View File

@ -340,20 +340,31 @@ void FGLRenderer::RenderScene(int recursion)
gl_RenderState.EnableFog(true);
gl_RenderState.BlendFunc(GL_ONE,GL_ZERO);
// First draw all single-pass stuff
gl_drawinfo->drawlists[GLDL_PLAIN].Sort();
gl_drawinfo->drawlists[GLDL_MASKED].Sort();
gl_drawinfo->drawlists[GLDL_MASKEDOFS].Sort();
// if we don't have a persistently mapped buffer, we have to process all the dynamic lights up front,
// so that we don't have to do repeated map/unmap calls on the buffer.
if (mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights && !(gl.flags & RFL_SHADER_STORAGE_BUFFER))
{
GLRenderer->mLights->Begin();
gl_drawinfo->drawlists[GLDL_PLAIN].Draw(GLPASS_LIGHTSONLY);
gl_drawinfo->drawlists[GLDL_MASKED].Draw(GLPASS_LIGHTSONLY);
gl_drawinfo->drawlists[GLDL_MASKEDOFS].Draw(GLPASS_LIGHTSONLY);
gl_drawinfo->drawlists[GLDL_TRANSLUCENTBORDER].Draw(GLPASS_LIGHTSONLY);
gl_drawinfo->drawlists[GLDL_TRANSLUCENT].Draw(GLPASS_LIGHTSONLY);
GLRenderer->mLights->Finish();
}
// Part 1: solid geometry. This is set up so that there are no transparent parts
glDepthFunc(GL_LESS);
gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f);
glDisable(GL_POLYGON_OFFSET_FILL); // just in case
GLRenderer->mLights->Finish();
glDisable(GL_POLYGON_OFFSET_FILL);
int pass;
if (mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights)
if (mLightCount > 0 && gl_fixedcolormap == CM_DEFAULT && gl_lights && (gl.flags & RFL_SHADER_STORAGE_BUFFER))
{
pass = GLPASS_ALL;
}
@ -364,7 +375,6 @@ void FGLRenderer::RenderScene(int recursion)
gl_RenderState.EnableTexture(gl_texture);
gl_RenderState.EnableBrightmap(true);
gl_drawinfo->drawlists[GLDL_PLAIN].Sort();
gl_drawinfo->drawlists[GLDL_PLAIN].Draw(pass);
@ -375,15 +385,13 @@ void FGLRenderer::RenderScene(int recursion)
gl_RenderState.SetTextureMode(TM_MASK);
}
gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold);
gl_drawinfo->drawlists[GLDL_MASKED].Sort();
gl_drawinfo->drawlists[GLDL_MASKED].Draw(pass);
// this list is empty most of the time so only waste time on it when in use.
// Part 3: masked geometry with polygon offset. This list is empty most of the time so only waste time on it when in use.
if (gl_drawinfo->drawlists[GLDL_MASKEDOFS].Size() > 0)
{
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
gl_drawinfo->drawlists[GLDL_MASKEDOFS].Sort();
gl_drawinfo->drawlists[GLDL_MASKEDOFS].Draw(pass);
glDisable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0, 0);
@ -393,7 +401,7 @@ void FGLRenderer::RenderScene(int recursion)
gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Draw decals (not a real pass)
// Part 4: Draw decals (not a real pass)
glDepthFunc(GL_LEQUAL);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
@ -413,8 +421,8 @@ void FGLRenderer::RenderScene(int recursion)
// so they don't interfere with overlapping mid textures.
glPolygonOffset(1.0f, 128.0f);
// flood all the gaps with the back sector's flat texture
// This will always be drawn like GLDL_PLAIN or GLDL_FOG, depending on the fog settings
// Part 5: flood all the gaps with the back sector's flat texture
// This will always be drawn like GLDL_PLAIN, depending on the fog settings
glDepthMask(false); // don't write to Z-buffer!
gl_RenderState.EnableFog(true);
@ -782,6 +790,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo
SetCameraPos(viewx, viewy, viewz, viewangle);
SetViewMatrix(false, false);
gl_RenderState.ApplyMatrices();
GLRenderer->mLights->Clear();
clipper.Clear();
angle_t a1 = FrustumAngle();

View File

@ -112,7 +112,7 @@ CVAR(Bool, gl_nolayer, false, 0)
//==========================================================================
void GLSprite::Draw(int pass)
{
if (pass == GLPASS_DECALS) return;
if (pass == GLPASS_DECALS || pass == GLPASS_LIGHTSONLY) return;

View File

@ -279,16 +279,17 @@ public:
int dynlightindex;
bool SetupSubsectorLights(bool lightsapplied, subsector_t * sub);
void SetupSubsectorLights(int pass, subsector_t * sub, int *dli = NULL);
void DrawSubsector(subsector_t * sub);
void DrawSubsectorLights(subsector_t * sub, int pass);
void DrawSubsectors(int pass, bool istrans);
void DrawSubsectors(int pass, bool processlights, bool istrans);
void ProcessLights(bool istrans);
void PutFlat(bool fog = false);
void Process(sector_t * model, int whichplane, bool notexture);
void SetFrom3DFloor(F3DFloor *rover, bool top, bool underside);
void ProcessSector(sector_t * frontsector);
void Draw(int pass);
void Draw(int pass, bool trans);
};

View File

@ -70,8 +70,19 @@ EXTERN_CVAR(Bool, gl_seamless)
//==========================================================================
FDynLightData lightdata;
void GLWall::SetupLights()
{
// check for wall types which cannot have dynamic lights on them (portal types never get here so they don't need to be checked.)
switch (type)
{
case RENDERWALL_FOGBOUNDARY:
case RENDERWALL_MIRRORSURFACE:
case RENDERWALL_COLOR:
case RENDERWALL_COLORLAYER:
return;
}
float vtx[]={glseg.x1,zbottom[0],glseg.y1, glseg.x1,ztop[0],glseg.y1, glseg.x2,ztop[1],glseg.y2, glseg.x2,zbottom[1],glseg.y2};
Plane p;
@ -357,7 +368,7 @@ void GLWall::Draw(int pass)
#endif
if (type == RENDERWALL_COLORLAYER)
if (type == RENDERWALL_COLORLAYER && pass != GLPASS_LIGHTSONLY)
{
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(-1.0f, -128.0f);
@ -365,10 +376,14 @@ void GLWall::Draw(int pass)
switch (pass)
{
case GLPASS_ALL: // Single-pass rendering
case GLPASS_LIGHTSONLY:
SetupLights();
break;
case GLPASS_ALL:
SetupLights();
// fall through
case GLPASS_PLAIN: // Single-pass rendering
case GLPASS_PLAIN:
rel = rellight + getExtraLight();
gl_SetColor(lightlevel, rel, Colormap,1.0f);
if (type!=RENDERWALL_M2SNF) gl_SetFog(lightlevel, rel, &Colormap, false);
@ -409,7 +424,7 @@ void GLWall::Draw(int pass)
}
}
if (type == RENDERWALL_COLORLAYER)
if (type == RENDERWALL_COLORLAYER && pass != GLPASS_LIGHTSONLY)
{
glDisable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0, 0);