From 1ec58011d2dc761043d19e9b098b3969cf26ed55 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 31 Jul 2014 00:44:22 +0200 Subject: [PATCH] - start of light buffer implementation so that we don't have to use uniform arrays which appear to be broken on AMD. --- src/CMakeLists.txt | 1 + src/gl/dynlights/gl_lightbuffer.cpp | 153 ++++++++++++++++++++++++++++ src/gl/dynlights/gl_lightbuffer.h | 30 ++++++ src/gl/scene/gl_wall.h | 2 +- src/gl/scene/gl_walls.cpp | 2 + src/gl/shaders/gl_shader.cpp | 10 +- 6 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 src/gl/dynlights/gl_lightbuffer.cpp create mode 100644 src/gl/dynlights/gl_lightbuffer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22fd833b45..fdc4bb6e46 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1088,6 +1088,7 @@ add_executable( zdoom WIN32 gl/dynlights/gl_dynlight.cpp gl/dynlights/gl_glow.cpp gl/dynlights/gl_dynlight1.cpp + gl/dynlights/gl_lightbuffer.cpp gl/shaders/gl_shader.cpp gl/shaders/gl_texshader.cpp gl/system/gl_interface.cpp diff --git a/src/gl/dynlights/gl_lightbuffer.cpp b/src/gl/dynlights/gl_lightbuffer.cpp new file mode 100644 index 0000000000..663631240c --- /dev/null +++ b/src/gl/dynlights/gl_lightbuffer.cpp @@ -0,0 +1,153 @@ +/* +** gl_lightbuffer.cpp +** Buffer data maintenance for dynamic lights +** +**--------------------------------------------------------------------------- +** Copyright 2014 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be +** covered by the terms of the GNU Lesser General Public License as published +** by the Free Software Foundation; either version 2.1 of the License, or (at +** your option) any later version. +** 5. Full disclosure of the entire project's source code, except for third +** party libraries is mandatory. (NOTE: This clause is non-negotiable!) +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "gl/system/gl_system.h" +#include "gl/dynlights/gl_lightbuffer.h" +#include "gl/dynlights/gl_dynlight.h" +#include "gl/system/gl_interface.h" + +static const int LIGHTBUF_BINDINGPOINT = 1; + +FLightBuffer::FLightBuffer() +{ + if (gl.flags & RFL_SHADER_STORAGE_BUFFER) + { + mBufferType = GL_SHADER_STORAGE_BUFFER; + mBufferSize = 80000; // 40000 lights per scene should be plenty. The largest I've ever seen was around 5000. + } + else + { + mBufferType = GL_UNIFORM_BUFFER; + mBufferSize = gl.maxuniformblock / 4 - 100; // we need to be a bit careful here so don't use the full buffer size + } + AddBuffer(); + Clear(); +} + +FLightBuffer::~FLightBuffer() +{ + glBindBuffer(mBufferType, 0); + for (unsigned int i = 0; i < mBufferIds.Size(); i++) + { + glDeleteBuffers(1, &mBufferIds[i]); + } +} + +void FLightBuffer::AddBuffer() +{ + unsigned int id; + glGenBuffers(1, &id); + mBufferIds.Push(id); + glBindBuffer(mBufferType, id); + unsigned int bytesize = mBufferSize * 8 * sizeof(float); + if (gl.flags & RFL_BUFFER_STORAGE) + { + 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); + mBufferPointers.Push((float*)map); + } + else + { + glBufferData(mBufferType, bytesize, NULL, GL_STREAM_DRAW); + } +} + +void FLightBuffer::Clear() +{ + mBufferNum = 0; + mIndex = 0; + mBufferArray.Clear(); + mBufferStart.Clear(); +} + +void FLightBuffer::UploadLights(FDynLightData &data, unsigned int &buffernum, unsigned int &bufferindex) +{ + int size0 = data.arrays[0].Size()/4; + int size1 = data.arrays[1].Size()/4; + int size2 = data.arrays[2].Size()/4; + int totalsize = size0 + size1 + size2 + 1; + + if (totalsize == 0) return; + + if (mIndex + totalsize > mBufferSize) + { + if (gl.flags & RFL_SHADER_STORAGE_BUFFER) + { + return; // we do not want multiple shader storage blocks. 40000 lights is too much already + } + else + { + mBufferNum++; + mBufferStart.Push(mIndex); + mIndex = 0; + if (mBufferIds.Size() <= mBufferNum) AddBuffer(); + } + } + + float *copyptr; + + if (gl.flags & RFL_BUFFER_STORAGE) + { + copyptr = mBufferPointers[mBufferNum] + mIndex * 4; + } + else + { + unsigned int pos = mBufferArray.Reserve(totalsize * 4); + copyptr = &mBufferArray[pos]; + } + + float parmcnt[] = { mIndex + 1, mIndex + 1 + size0, mIndex + 1 + size0 + size1, mIndex + totalsize }; + + memcpy(©ptr[0], parmcnt, 4 * sizeof(float)); + memcpy(©ptr[1], &data.arrays[0][0], 4 * size0*sizeof(float)); + memcpy(©ptr[1 + size0], &data.arrays[1][0], 4 * size1*sizeof(float)); + memcpy(©ptr[1 + size0 + size1], &data.arrays[2][0], 4 * size2*sizeof(float)); + buffernum = mBufferNum; + bufferindex = mIndex; + mIndex += totalsize; +} + +void FLightBuffer::Finish() +{ + //glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, mBufferIds[0]); +} + + + diff --git a/src/gl/dynlights/gl_lightbuffer.h b/src/gl/dynlights/gl_lightbuffer.h new file mode 100644 index 0000000000..57e6bf0c82 --- /dev/null +++ b/src/gl/dynlights/gl_lightbuffer.h @@ -0,0 +1,30 @@ +#ifndef __GL_LIGHTBUFFER_H +#define __GL_LIGHTBUFFER_H + +#include "tarray.h" +struct FDynLightData; + +class FLightBuffer +{ + TArray mBufferArray; + TArray mBufferIds; + TArray mBufferStart; + TArray mBufferPointers; + unsigned int mBufferType; + unsigned int mBufferSize; + unsigned int mIndex; + unsigned int mBufferNum; + + void AddBuffer(); + +public: + + FLightBuffer(); + ~FLightBuffer(); + void Clear(); + void UploadLights(FDynLightData &data, unsigned int &buffernum, unsigned int &bufferindex); + void Finish(); +}; + +#endif + diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h index fafd5ccb85..e17f61c3e1 100644 --- a/src/gl/scene/gl_wall.h +++ b/src/gl/scene/gl_wall.h @@ -135,7 +135,7 @@ public: float topglowcolor[4]; float bottomglowcolor[4]; - int firstdynlight, lastdynlight; + unsigned int dynlightindex, dynlightbuffer; int firstwall, numwalls; // splitting info. union diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp index e665eeb894..cd179a2653 100644 --- a/src/gl/scene/gl_walls.cpp +++ b/src/gl/scene/gl_walls.cpp @@ -1486,6 +1486,7 @@ void GLWall::Process(seg_t *seg, sector_t * frontsector, sector_t * backsector) glseg.y2= FIXED2FLOAT(v2->y); Colormap=frontsector->ColorMap; flags = 0; + dynlightindex = UINT_MAX; int rel = 0; int orglightlevel = gl_ClampLight(frontsector->lightlevel); @@ -1750,6 +1751,7 @@ void GLWall::ProcessLowerMiniseg(seg_t *seg, sector_t * frontsector, sector_t * bottomflat = frontsector->GetTexture(sector_t::floor); topplane = frontsector->ceilingplane; bottomplane = frontsector->floorplane; + dynlightindex = UINT_MAX; zfloor[0] = zfloor[1] = FIXED2FLOAT(ffh); diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 20abe17e56..1ddb58076a 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -264,7 +264,7 @@ FShader *FShaderManager::Compile (const char *ShaderName, const char *ShaderPath FString defines; // this can't be in the shader code due to ATI strangeness. if (gl.MaxLights() == 128) defines += "#define MAXLIGHTS128\n"; - if (!usediscard) defines += "#define NO_DISCARD\n"; + if (!usediscard) defines += "#define NO_ALPHATEST\n"; FShader *shader = NULL; try @@ -350,10 +350,10 @@ struct FEffectShader static const FEffectShader effectshaders[]= { - { "fogboundary", "shaders/glsl/main.vp", "shaders/glsl/fogboundary.fp", NULL, "" }, - { "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "#define SPHEREMAP\n" }, - { "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", NULL, "#define SIMPLE\n" }, - { "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", NULL, "#define SIMPLE\n" }, + { "fogboundary", "shaders/glsl/main.vp", "shaders/glsl/fogboundary.fp", NULL, "#define NO_ALPHATEST\n" }, + { "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" }, + { "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", NULL, "#define SIMPLE\n#define NO_ALPHATEST\n" }, + { "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", NULL, "#define SIMPLE\n#define NO_ALPHATEST\n" }, };