diff --git a/src/modules/graphics/opengl/Font.cpp b/src/modules/graphics/opengl/Font.cpp index e1370ecd9..8296f9590 100644 --- a/src/modules/graphics/opengl/Font.cpp +++ b/src/modules/graphics/opengl/Font.cpp @@ -532,7 +532,7 @@ void Font::drawVertices(const std::vector &drawcommands) totalverts = std::max(cmd.startvertex + cmd.vertexcount, totalverts); if ((size_t) totalverts / 4 > quadIndices.getSize()) - quadIndices = VertexIndex((size_t) totalverts / 4); + quadIndices = QuadIndices((size_t) totalverts / 4); gl.prepareDraw(); diff --git a/src/modules/graphics/opengl/Font.h b/src/modules/graphics/opengl/Font.h index d7f031a31..6621a38fa 100644 --- a/src/modules/graphics/opengl/Font.h +++ b/src/modules/graphics/opengl/Font.h @@ -236,7 +236,7 @@ private: bool useSpacesAsTab; // Index buffer used for drawing quads with GL_TRIANGLES. - VertexIndex quadIndices; + QuadIndices quadIndices; // ID which is incremented when the texture cache is invalidated. uint32 textureCacheID; diff --git a/src/modules/graphics/opengl/GLBuffer.cpp b/src/modules/graphics/opengl/GLBuffer.cpp index 86fcca92d..b13ff89fe 100644 --- a/src/modules/graphics/opengl/GLBuffer.cpp +++ b/src/modules/graphics/opengl/GLBuffer.cpp @@ -198,164 +198,127 @@ void GLBuffer::unload() } -// VertexIndex +// QuadIndices -size_t VertexIndex::maxSize = 0; -size_t VertexIndex::elementSize = 0; -std::list VertexIndex::sizeRefs; -GLBuffer *VertexIndex::element_array = NULL; +size_t QuadIndices::maxSize = 0; +size_t QuadIndices::elementSize = 0; +size_t QuadIndices::objectCount = 0; +GLBuffer *QuadIndices::indexBuffer = nullptr; -VertexIndex::VertexIndex(size_t size) +QuadIndices::QuadIndices(size_t size) : size(size) { // The upper limit is the maximum of GLuint divided by six (the number // of indices per size) and divided by the size of GLuint. This guarantees // no overflows when calculating the array size in bytes. - // Memory issues will be handled by other exceptions. if (size == 0 || size > ((GLuint) -1) / 6 / sizeof(GLuint)) - throw love::Exception("Invalid size."); + throw love::Exception("Invalid number of quads."); - addSize(size); + // Create a new / larger buffer if needed. + if (indexBuffer == nullptr || size > maxSize) + { + GLBuffer *newbuffer = nullptr; + + // Depending on the size, a switch to int and more memory is needed. + GLenum targettype = getType(size); + size_t elemsize = (targettype == GL_UNSIGNED_SHORT) ? sizeof(GLushort) : sizeof(GLuint); + + size_t buffersize = elemsize * 6 * size; + + // Create may throw out-of-memory exceptions. + // QuadIndices will propagate the exception and keep the old GLBuffer. + try + { + newbuffer = new GLBuffer(buffersize, nullptr, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW); + } + catch (std::bad_alloc &) + { + throw love::Exception("Out of memory."); + } + + // Allocation of the new GLBuffer succeeded. + // The old GLBuffer can now be deleted. + delete indexBuffer; + indexBuffer = newbuffer; + maxSize = size; + elementSize = elemsize; + + switch (targettype) + { + case GL_UNSIGNED_SHORT: + fill(); + break; + case GL_UNSIGNED_INT: + fill(); + break; + } + } + + objectCount++; } -VertexIndex::VertexIndex(const VertexIndex &other) +QuadIndices::QuadIndices(const QuadIndices &other) : size(other.size) { - addSize(size); + objectCount++; } -VertexIndex &VertexIndex::operator = (const VertexIndex &other) +QuadIndices &QuadIndices::operator = (const QuadIndices &other) { - addSize(other.size); - removeSize(size); size = other.size; return *this; } -VertexIndex::~VertexIndex() +QuadIndices::~QuadIndices() { - removeSize(size); + --objectCount; + + // Delete the buffer if we were the last living QuadIndices object. + if (objectCount <= 0) + { + delete indexBuffer; + indexBuffer = nullptr; + } } -size_t VertexIndex::getSize() const +size_t QuadIndices::getSize() const { return size; } -size_t VertexIndex::getIndexCount(size_t elements) const +size_t QuadIndices::getIndexCount(size_t elements) const { return elements * 6; } -GLenum VertexIndex::getType(size_t s) const +GLenum QuadIndices::getType(size_t s) const { // Calculates if unsigned short is big enough to hold all the vertex indices. - static const GLenum type_table[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT}; - return type_table[s * 4 > std::numeric_limits::max()]; + static const GLenum types[] = {GL_UNSIGNED_SHORT, GL_UNSIGNED_INT}; + return types[s * 4 > std::numeric_limits::max()]; // if buffer-size > max(GLushort) then GL_UNSIGNED_INT else GL_UNSIGNED_SHORT } -size_t VertexIndex::getElementSize() +size_t QuadIndices::getElementSize() { return elementSize; } -GLBuffer *VertexIndex::getBuffer() const +GLBuffer *QuadIndices::getBuffer() const { - return element_array; + return indexBuffer; } -const void *VertexIndex::getPointer(size_t offset) const +const void *QuadIndices::getPointer(size_t offset) const { - return element_array->getPointer(offset); -} - -void VertexIndex::addSize(size_t newSize) -{ - if (newSize <= maxSize) - { - // Current size is bigger. Append the size to list and sort. - sizeRefs.push_back(newSize); - sizeRefs.sort(); - return; - } - - // Try to resize before adding it to the list because resize may throw. - resize(newSize); - sizeRefs.push_back(newSize); -} - -void VertexIndex::removeSize(size_t oldSize) -{ - // TODO: For debugging purposes, this should check if the size was actually found. - sizeRefs.erase(std::find(sizeRefs.begin(), sizeRefs.end(), oldSize)); - if (sizeRefs.size() == 0) - { - resize(0); - return; - } - - if (oldSize == maxSize) - { - // Shrink if there's a smaller size. - size_t newSize = sizeRefs.back(); - if (newSize < maxSize) - resize(newSize); - } -} - -void VertexIndex::resize(size_t size) -{ - if (size == 0) - { - delete element_array; - element_array = NULL; - maxSize = 0; - return; - } - - GLBuffer *new_element_array; - - // Depending on the size, a switch to int and more memory is needed. - GLenum target_type = getType(size); - size_t elem_size = (target_type == GL_UNSIGNED_SHORT) ? sizeof(GLushort) : sizeof(GLuint); - - size_t array_size = elem_size * 6 * size; - - // Create may throw out-of-memory exceptions. - // VertexIndex will propagate the exception and keep the old GLBuffer. - try - { - new_element_array = new GLBuffer(array_size, nullptr, GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW); - } - catch (std::bad_alloc &) - { - throw love::Exception("Out of memory."); - } - - // Allocation of the new GLBuffer succeeded. - // The old GLBuffer can now be deleted. - delete element_array; - element_array = new_element_array; - maxSize = size; - elementSize = elem_size; - - switch (target_type) - { - case GL_UNSIGNED_SHORT: - fill(); - break; - case GL_UNSIGNED_INT: - fill(); - break; - } + return indexBuffer->getPointer(offset); } template -void VertexIndex::fill() +void QuadIndices::fill() { - GLBuffer::Bind bind(*element_array); - GLBuffer::Mapper mapper(*element_array); + GLBuffer::Bind bind(*indexBuffer); + GLBuffer::Mapper mapper(*indexBuffer); T *indices = (T *) mapper.get(); diff --git a/src/modules/graphics/opengl/GLBuffer.h b/src/modules/graphics/opengl/GLBuffer.h index aac2bbf48..44a95e2da 100644 --- a/src/modules/graphics/opengl/GLBuffer.h +++ b/src/modules/graphics/opengl/GLBuffer.h @@ -266,7 +266,7 @@ private: /** - * VertexIndex manages one shared GLBuffer that stores the indices for an + * QuadIndices manages one shared GLBuffer that stores the indices for an * element array. Vertex arrays using the vertex structure (or anything else * that can use the pattern below) can request a size and use it for the * drawElements call. @@ -280,14 +280,14 @@ private: * indices[i*6 + 5] = i*4 + 3; * * There will always be a large enough GLBuffer around until all - * VertexIndex instances have been deleted. + * QuadIndices instances have been deleted. * - * Q: Why have something like VertexIndex? + * Q: Why have something like QuadIndices? * A: The indices for the SpriteBatch do not change, only the array size * varies. Using one GLBuffer for all element arrays removes this * duplicated data and saves some memory. */ -class VertexIndex +class QuadIndices { public: /** @@ -297,16 +297,16 @@ public: * * @param size The requested size in groups of 6 indices. */ - VertexIndex(size_t size); + QuadIndices(size_t size); - VertexIndex(const VertexIndex &other); - VertexIndex &operator = (const VertexIndex &other); + QuadIndices(const QuadIndices &other); + QuadIndices &operator = (const QuadIndices &other); /** * Removes an entry from the list of sizes and resizes the GLBuffer * if needed. */ - ~VertexIndex(); + ~QuadIndices(); /** * Returns the number of index groups. @@ -318,8 +318,8 @@ public: /** * Returns the number of indices that the passed element count will have. - * Use VertexIndex::getSize to get the full index count for that - * VertexIndex instance. + * Use QuadIndices::getSize to get the full index count for that + * QuadIndices instance. * * @param elements The number of elements to calculate the index count for. * @return The index count. @@ -369,30 +369,6 @@ public: private: - /** - * Adds a new size to the size list, then sorts and resizes it if needed. - * - * @param newSize The new size to be added. - */ - void addSize(size_t newSize); - - /** - * Removes a size from the size list, then sorts and resizes it if needed. - * - * @param oldSize The old size to be removed. - */ - void removeSize(size_t oldSize); - - /** - * Resizes the GLBuffer to the requested size. - * This function takes care of choosing the correct integer type and - * allocating and deleting the GLBuffer instance. It also has some - * fallback logic in case the memory ran out. - * - * @param size The requested GLBuffer size. Passing 0 deletes the GLBuffer without allocating a new one. - */ - void resize(size_t size); - /** * Adds all indices to the array with the type T. * There are no checks for the correct types or overflows. The calling @@ -405,12 +381,14 @@ private: // The size in bytes of an element in the element array. static size_t elementSize; + // The current GLBuffer size. 0 means no GLBuffer. static size_t maxSize; - // The list of sizes. Needs to be kept sorted in ascending order. - static std::list sizeRefs; - // The GLBuffer for the element array. Can be NULL. - static GLBuffer *element_array; + + static size_t objectCount; + + // The GLBuffer for the element array. Can be null. + static GLBuffer *indexBuffer; }; } // opengl diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index c7e59facb..86e314d8d 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -50,7 +50,8 @@ namespace opengl { Graphics::Graphics() - : width(0) + : quadIndices(nullptr) + , width(0) , height(0) , created(false) , active(true) @@ -84,6 +85,9 @@ Graphics::~Graphics() Shader::defaultShader = nullptr; } + if (quadIndices) + delete quadIndices; + currentWindow->release(); } @@ -289,6 +293,14 @@ bool Graphics::setMode(int width, int height, bool &sRGB) setDebug(enabledebug); + // Create a quad indices object owned by love.graphics, so at least one + // QuadIndices object is alive at all times while love.graphics is alive. + // This makes sure there aren't too many expensive destruction/creations of + // index buffer objects, since the shared index buffer used by QuadIndices + // objects is destroyed when the last object is destroyed. + if (quadIndices == nullptr) + quadIndices = new QuadIndices(20); + // Reload all volatile objects. if (!Volatile::loadAll()) ::printf("Could not reload all volatile objects.\n"); @@ -296,9 +308,9 @@ bool Graphics::setMode(int width, int height, bool &sRGB) // Restore the graphics state. restoreState(states.back()); - pixel_size_stack.clear(); - pixel_size_stack.reserve(5); - pixel_size_stack.push_back(1); + pixelSizeStack.clear(); + pixelSizeStack.reserve(5); + pixelSizeStack.push_back(1); // We always need a default shader. if (!Shader::defaultShader) @@ -1089,19 +1101,19 @@ void Graphics::polyline(const float *coords, size_t count) if (state.lineJoin == LINE_JOIN_NONE) { NoneJoinPolyline line; - line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixelSizeStack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } else if (state.lineJoin == LINE_JOIN_BEVEL) { BevelJoinPolyline line; - line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixelSizeStack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } else // LINE_JOIN_MITER { MiterJoinPolyline line; - line.render(coords, count, state.lineWidth * .5f, float(pixel_size_stack.back()), state.lineStyle == LINE_SMOOTH); + line.render(coords, count, state.lineWidth * .5f, float(pixelSizeStack.back()), state.lineStyle == LINE_SMOOTH); line.draw(); } } @@ -1424,7 +1436,7 @@ void Graphics::push(StackType type) gl.pushTransform(); - pixel_size_stack.push_back(pixel_size_stack.back()); + pixelSizeStack.push_back(pixelSizeStack.back()); if (type == STACK_ALL) states.push_back(states.back()); @@ -1438,7 +1450,7 @@ void Graphics::pop() throw Exception("Minimum stack depth reached (more pops than pushes?)"); gl.popTransform(); - pixel_size_stack.pop_back(); + pixelSizeStack.pop_back(); if (stackTypes.back() == STACK_ALL) { @@ -1461,7 +1473,7 @@ void Graphics::rotate(float r) void Graphics::scale(float x, float y) { gl.getTransform().scale(x, y); - pixel_size_stack.back() *= 2. / (fabs(x) + fabs(y)); + pixelSizeStack.back() *= 2. / (fabs(x) + fabs(y)); } void Graphics::translate(float x, float y) @@ -1477,7 +1489,7 @@ void Graphics::shear(float kx, float ky) void Graphics::origin() { gl.getTransform().setIdentity(); - pixel_size_stack.back() = 1; + pixelSizeStack.back() = 1; } Graphics::DisplayState::DisplayState() diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 236148aab..7233b506f 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -511,7 +511,9 @@ private: StrongRef defaultFont; - std::vector pixel_size_stack; // stores current size of a pixel (needed for line drawing) + std::vector pixelSizeStack; // stores current size of a pixel (needed for line drawing) + + QuadIndices *quadIndices; int width; int height; diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index 41da73b05..a29bd4966 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -209,7 +209,7 @@ void ParticleSystem::setBufferSize(uint32 size) { if (size == 0 || size > MAX_PARTICLES) throw love::Exception("Invalid buffer size"); - quadIndices = VertexIndex(size); + quadIndices = QuadIndices(size); deleteBuffers(); createBuffers(size); reset(); diff --git a/src/modules/graphics/opengl/ParticleSystem.h b/src/modules/graphics/opengl/ParticleSystem.h index d9c497f6d..05c16fcfc 100644 --- a/src/modules/graphics/opengl/ParticleSystem.h +++ b/src/modules/graphics/opengl/ParticleSystem.h @@ -577,7 +577,7 @@ protected: Vertex *particleVerts; // Vertex index buffer. - VertexIndex quadIndices; + QuadIndices quadIndices; // The texture to be drawn. StrongRef texture; diff --git a/src/modules/graphics/opengl/SpriteBatch.cpp b/src/modules/graphics/opengl/SpriteBatch.cpp index cb9459fcc..c57dc902e 100644 --- a/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/src/modules/graphics/opengl/SpriteBatch.cpp @@ -189,7 +189,7 @@ void SpriteBatch::setBufferSize(int newsize) void *new_data = new_array_buf->map(); memcpy(new_data, old_data, sizeof(Vertex) * 4 * std::min(newsize, size)); - quad_indices = VertexIndex(newsize); + quad_indices = QuadIndices(newsize); } catch (love::Exception &) { diff --git a/src/modules/graphics/opengl/SpriteBatch.h b/src/modules/graphics/opengl/SpriteBatch.h index da00a0cf4..b4ba1df53 100644 --- a/src/modules/graphics/opengl/SpriteBatch.h +++ b/src/modules/graphics/opengl/SpriteBatch.h @@ -127,7 +127,7 @@ private: Color *color; GLBuffer *array_buf; - VertexIndex quad_indices; + QuadIndices quad_indices; // The portion of the vertex buffer that's been modified while mapped. size_t buffer_used_offset;