Reinstate light buffer resizing, using the same method as for the model matrix buffer

This commit is contained in:
Christoph Oelckers 2018-08-14 12:19:39 +02:00
parent ad80efd6be
commit 6f81310fa7
2 changed files with 92 additions and 13 deletions

View file

@ -38,7 +38,7 @@ static const int ELEMENT_SIZE = (4*sizeof(float));
FLightBuffer::FLightBuffer() FLightBuffer::FLightBuffer()
{ {
int maxNumberOfLights = gl.lightmethod == LM_DIRECT? 80000 : 40000; int maxNumberOfLights = 40000;
mBufferSize = maxNumberOfLights * ELEMENTS_PER_LIGHT; mBufferSize = maxNumberOfLights * ELEMENTS_PER_LIGHT;
mByteSize = mBufferSize * ELEMENT_SIZE; mByteSize = mBufferSize * ELEMENT_SIZE;
@ -91,6 +91,56 @@ void FLightBuffer::Clear()
mIndex = 0; mIndex = 0;
} }
void FLightBuffer::CheckSize()
{
// 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(mBufferType, newbuffer); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should.
glBindBuffer(GL_COPY_READ_BUFFER, mBufferId);
// create the new buffer's storage (twice as large as the old one)
int oldbytesize = mByteSize;
unsigned int bufferbytesize = mBufferedData.Size() * 4;
if (bufferbytesize > mByteSize)
{
mByteSize += bufferbytesize;
}
else
{
mByteSize *= 2;
}
mBufferSize = mByteSize / ELEMENT_SIZE;
if (gl.lightmethod == LM_DIRECT)
{
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;
Begin();
memcpy(mBufferPointer + mBlockSize*4, &mBufferedData[0], bufferbytesize);
mBufferedData.Clear();
Finish();
}
int FLightBuffer::UploadLights(FDynLightData &data) int FLightBuffer::UploadLights(FDynLightData &data)
{ {
// All meaasurements here are in vec4's. // All meaasurements here are in vec4's.
@ -119,21 +169,35 @@ int FLightBuffer::UploadLights(FDynLightData &data)
assert(mBufferPointer != nullptr); assert(mBufferPointer != nullptr);
if (mBufferPointer == nullptr) return -1; if (mBufferPointer == nullptr) return -1;
if (totalsize <= 1) return -1; // there are no lights
if (totalsize <= 4 || mIndex + totalsize > mBufferSize) return -1; unsigned thisindex = mIndex.fetch_add(totalsize);
auto thisindex = mIndex.fetch_add(totalsize); float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) };
if (thisindex + totalsize > mBufferSize) return -1; // must retest because another thread might have changed mIndex.
if (thisindex + totalsize <= mBufferSize)
{
float *copyptr = mBufferPointer + thisindex*4; float *copyptr = mBufferPointer + thisindex*4;
float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) };
memcpy(&copyptr[0], parmcnt, ELEMENT_SIZE); memcpy(&copyptr[0], parmcnt, ELEMENT_SIZE);
memcpy(&copyptr[4], &data.arrays[0][0], size0 * ELEMENT_SIZE); memcpy(&copyptr[4], &data.arrays[0][0], size0 * ELEMENT_SIZE);
memcpy(&copyptr[4 + 4*size0], &data.arrays[1][0], size1 * ELEMENT_SIZE); memcpy(&copyptr[4 + 4*size0], &data.arrays[1][0], size1 * ELEMENT_SIZE);
memcpy(&copyptr[4 + 4*(size0 + size1)], &data.arrays[2][0], size2 * ELEMENT_SIZE); memcpy(&copyptr[4 + 4*(size0 + size1)], &data.arrays[2][0], size2 * ELEMENT_SIZE);
return thisindex; return thisindex;
} }
else
{
// The buffered data always starts at the old buffer's end to avoid more extensive synchronization.
std::lock_guard<std::mutex> lock(mBufferMutex);
auto index = mBufferedData.Reserve(totalsize);
float *copyptr =&mBufferedData[index];
// The copy operation must be inside the mutexed block of code here!
memcpy(&copyptr[0], parmcnt, ELEMENT_SIZE);
memcpy(&copyptr[4], &data.arrays[0][0], size0 * ELEMENT_SIZE);
memcpy(&copyptr[4 + 4*size0], &data.arrays[1][0], size1 * ELEMENT_SIZE);
memcpy(&copyptr[4 + 4*(size0 + size1)], &data.arrays[2][0], size2 * ELEMENT_SIZE);
return mBufferSize + index;
}
}
void FLightBuffer::Begin() void FLightBuffer::Begin()
{ {
@ -152,6 +216,7 @@ void FLightBuffer::Finish()
glUnmapBuffer(mBufferType); glUnmapBuffer(mBufferType);
mBufferPointer = NULL; mBufferPointer = NULL;
} }
if (mBufferedData.Size() > 0) CheckSize();
} }
int FLightBuffer::BindUBO(unsigned int index) int FLightBuffer::BindUBO(unsigned int index)

View file

@ -4,6 +4,7 @@
#include "tarray.h" #include "tarray.h"
#include "hwrenderer/dynlights/hw_dynlightdata.h" #include "hwrenderer/dynlights/hw_dynlightdata.h"
#include <atomic> #include <atomic>
#include <mutex>
class FLightBuffer class FLightBuffer
{ {
@ -19,6 +20,9 @@ class FLightBuffer
unsigned int mByteSize; unsigned int mByteSize;
unsigned int mMaxUploadSize; unsigned int mMaxUploadSize;
std::mutex mBufferMutex;
TArray<float> mBufferedData;
public: public:
FLightBuffer(); FLightBuffer();
@ -27,9 +31,19 @@ public:
int UploadLights(FDynLightData &data); int UploadLights(FDynLightData &data);
void Begin(); void Begin();
void Finish(); void Finish();
void CheckSize();
int BindUBO(unsigned int index); int BindUBO(unsigned int index);
unsigned int GetBlockSize() const { return mBlockSize; } unsigned int GetBlockSize() const { return mBlockSize; }
unsigned int GetBufferType() const { return mBufferType; } unsigned int GetBufferType() const { return mBufferType; }
int ShaderIndex(unsigned int index) const
{
if (mBlockAlign == 0) return index;
// This must match the math in BindUBO.
unsigned int offset = (index / mBlockAlign) * mBlockAlign;
return int(index-offset);
}
}; };
int gl_SetDynModelLight(AActor *self, int dynlightindex); int gl_SetDynModelLight(AActor *self, int dynlightindex);