Simplify quad drawing code, and use glDrawElementsBaseVertex when available.

This commit is contained in:
Alex Szpakowski
2017-12-24 22:05:10 -04:00
parent 785902d8bd
commit a31b500b03
14 changed files with 135 additions and 185 deletions
-103
View File
@@ -18,12 +18,7 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
#include "common/config.h"
#include "Buffer.h"
#include "Graphics.h"
#include "common/Exception.h"
#include <algorithm> // std::min
namespace love
{
@@ -43,103 +38,5 @@ Buffer::~Buffer()
{
}
// QuadIndices
static const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
static const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
size_t QuadIndices::objectCount = 0;
Buffer *QuadIndices::indexBuffer = nullptr;
QuadIndices::QuadIndices(Graphics *gfx)
{
if (indexBuffer == nullptr)
{
size_t buffersize = sizeof(uint16) * MAX_QUADS_PER_DRAW * 6;
indexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0);
Buffer::Mapper map(*indexBuffer);
vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, MAX_VERTICES_PER_DRAW, (uint16 *) map.get());
}
objectCount++;
}
QuadIndices::QuadIndices(const QuadIndices &)
{
objectCount++;
}
QuadIndices &QuadIndices::operator = (const QuadIndices &)
{
return *this;
}
QuadIndices::~QuadIndices()
{
--objectCount;
// Delete the buffer if we were the last living QuadIndices object.
if (objectCount <= 0)
{
delete indexBuffer;
indexBuffer = nullptr;
}
}
static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::Buffers &buffers, int vertexcount)
{
// TODO: Figure out a better way to avoid touching the same buffer multiple
// times, if multiple attributes share the buffer.
uint32 touchedbuffers = 0;
for (unsigned int i = 0; i < vertex::Attributes::MAX; i++)
{
if (!attributes.isEnabled(i))
continue;
auto &attrib = attributes.attribs[i];
uint32 bufferbit = 1u << attrib.bufferindex;
if ((touchedbuffers & bufferbit) == 0)
{
touchedbuffers |= bufferbit;
buffers.info[attrib.bufferindex].offset += attrib.stride * vertexcount;
}
}
}
void QuadIndices::draw(Graphics *gfx, int quadstart, int quadcount, const vertex::Attributes &attributes, vertex::Buffers buffers, Texture *texture)
{
Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexBuffer);
cmd.primitiveType = PRIMITIVE_TRIANGLES;
cmd.indexBufferOffset = 0;
cmd.indexType = INDEX_UINT16;
cmd.texture = texture;
// TODO: We can use glDrawElementsBaseVertex when supported, instead of
// advancing the vertex offset.
if (quadstart > 0)
advanceVertexOffsets(attributes, buffers, quadstart * 4);
for (int quadindex = 0; quadindex < quadcount; quadindex += MAX_QUADS_PER_DRAW)
{
int quaddrawcount = std::min(MAX_QUADS_PER_DRAW, quadcount - quadindex);
if (quadindex > 0)
advanceVertexOffsets(attributes, buffers, quaddrawcount * 4);
cmd.indexCount = quaddrawcount * 6;
gfx->draw(cmd);
}
}
Buffer *QuadIndices::getBuffer() const
{
return indexBuffer;
}
} // graphics
} // love
-55
View File
@@ -34,9 +34,6 @@ namespace love
namespace graphics
{
class Graphics;
class Texture;
/**
* A block of GPU-owned memory. Currently meant for internal use.
**/
@@ -150,57 +147,5 @@ protected:
}; // Buffer
/**
* QuadIndices manages one shared Buffer 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 indexed draw
* call.
*
* indices[i*6 + 0] = i*4 + 0;
* indices[i*6 + 1] = i*4 + 1;
* indices[i*6 + 2] = i*4 + 2;
*
* indices[i*6 + 3] = i*4 + 2;
* indices[i*6 + 4] = i*4 + 1;
* indices[i*6 + 5] = i*4 + 3;
*
* There will always be a large enough Buffer around until all QuadIndices
* instances have been deleted.
*
* Q: Why have something like QuadIndices?
* A: The indices for the SpriteBatch do not change, only the array size varies.
* Using one Buffer for all element arrays removes this duplicated data and
* saves some memory.
*/
class QuadIndices
{
public:
/**
* Adds an entry to the list of sizes and resizes the Buffer
* if needed. A size of 1 allocates a group of 6 indices for 4 vertices
* creating 1 face.
*/
QuadIndices(Graphics *gfx);
QuadIndices(const QuadIndices &other);
QuadIndices &operator = (const QuadIndices &other);
/**
* Removes an entry from the list of sizes and resizes the GLBuffer
* if needed.
*/
~QuadIndices();
void draw(Graphics *gfx, int quadstart, int quadcount, const vertex::Attributes &attributes, vertex::Buffers buffers, Texture *texture);
Buffer *getBuffer() const;
private:
static size_t objectCount;
static Buffer *indexBuffer;
}; // QuadIndices
} // graphics
} // love
+15
View File
@@ -119,6 +119,7 @@ Graphics::Graphics()
, canvasSwitchCount(0)
, drawCalls(0)
, drawCallsBatched(0)
, quadIndexBuffer(nullptr)
, capabilities()
, cachedShaderStages()
{
@@ -134,6 +135,8 @@ Graphics::Graphics()
Graphics::~Graphics()
{
delete quadIndexBuffer;
// Clean up standard shaders before the active shader. If we do it after,
// the active shader may try to activate a standard shader when deactivating
// itself, which will cause problems since it calls Graphics methods in the
@@ -161,6 +164,18 @@ Graphics::~Graphics()
Shader::deinitialize();
}
void Graphics::createQuadIndexBuffer()
{
if (quadIndexBuffer != nullptr)
return;
size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6;
quadIndexBuffer = newBuffer(size, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0);
Buffer::Mapper map(*quadIndexBuffer);
vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get());
}
Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh)
{
return new Quad(v, sw, sh);
+5
View File
@@ -851,6 +851,7 @@ public:
virtual void draw(const DrawCommand &cmd) = 0;
virtual void draw(const DrawIndexedCommand &cmd) = 0;
virtual void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, Texture *texture) = 0;
void flushStreamDraws();
StreamVertexData requestStreamDraw(const StreamDrawCommand &command);
@@ -996,6 +997,8 @@ protected:
virtual void initCapabilities() = 0;
virtual void getAPIStats(int &shaderswitches) const = 0;
void createQuadIndexBuffer();
Canvas *getTemporaryCanvas(PixelFormat format, int w, int h, int samples);
void restoreState(const DisplayState &s);
@@ -1035,6 +1038,8 @@ protected:
int drawCalls;
int drawCallsBatched;
Buffer *quadIndexBuffer;
Capabilities capabilities;
Deprecations deprecations;
+1 -5
View File
@@ -95,7 +95,6 @@ ParticleSystem::ParticleSystem(Graphics *gfx, Texture *texture, uint32 size)
, relativeRotation(false)
, vertexAttributes(vertex::CommonFormat::XYf_STf_RGBAub, 0)
, buffer(nullptr)
, quadIndices(gfx)
{
if (size == 0 || size > MAX_PARTICLES)
throw love::Exception("Invalid ParticleSystem size.");
@@ -157,7 +156,6 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p)
, relativeRotation(p.relativeRotation)
, vertexAttributes(p.vertexAttributes)
, buffer(nullptr)
, quadIndices(p.quadIndices)
{
setBufferSize(maxParticles);
}
@@ -194,8 +192,6 @@ void ParticleSystem::createBuffers(size_t size)
size_t bytes = sizeof(Vertex) * size * 4;
buffer = gfx->newBuffer(bytes, nullptr, BUFFER_VERTEX, vertex::USAGE_STREAM, 0);
quadIndices = QuadIndices(gfx);
}
catch (std::bad_alloc &)
{
@@ -1101,7 +1097,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m)
vertex::Buffers vertexbuffers;
vertexbuffers.set(0, buffer, 0);
quadIndices.draw(gfx, 0, pCount, vertexAttributes, vertexbuffers, texture);
gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, texture);
}
bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out)
-3
View File
@@ -702,9 +702,6 @@ private:
const vertex::Attributes vertexAttributes;
Buffer *buffer;
// Vertex index buffer.
QuadIndices quadIndices;
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM>::Entry distributionsEntries[];
static StringMap<AreaSpreadDistribution, DISTRIBUTION_MAX_ENUM> distributions;
+1 -2
View File
@@ -47,7 +47,6 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag
, color(255, 255, 255, 255)
, color_active(false)
, array_buf(nullptr)
, quad_indices(gfx)
, range_start(-1)
, range_count(-1)
{
@@ -394,7 +393,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m)
count = std::min(count, next - start);
if (count > 0)
quad_indices.draw(gfx, start, count, attributes, buffers, texture);
gfx->drawQuads(start, count, attributes, buffers, texture);
}
} // graphics
+1 -2
View File
@@ -140,8 +140,7 @@ private:
size_t vertex_stride;
love::graphics::Buffer *array_buf;
QuadIndices quad_indices;
std::unordered_map<std::string, AttachedAttribute> attached_attributes;
int range_start;
+1 -2
View File
@@ -34,7 +34,6 @@ Text::Text(Graphics *gfx, Font *font, const std::vector<Font::ColoredString> &te
: font(font)
, vertexAttributes(Font::vertexFormat, 0)
, vbo(nullptr)
, quadIndices(gfx)
, vert_offset(0)
, texture_cache_id((uint32) -1)
{
@@ -264,7 +263,7 @@ void Text::draw(Graphics *gfx, const Matrix4 &m)
Graphics::TempTransform transform(gfx, m);
for (const Font::DrawCommand &cmd : draw_commands)
quadIndices.draw(gfx, cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture);
gfx->drawQuads(cmd.startvertex / 4, cmd.vertexcount / 4, vertexAttributes, vertexBuffers, cmd.texture);
}
} // graphics
-1
View File
@@ -89,7 +89,6 @@ private:
vertex::Buffers vertexBuffers;
Buffer *vbo;
QuadIndices quadIndices;
std::vector<Font::DrawCommand> draw_commands;
+72 -11
View File
@@ -55,8 +55,7 @@ namespace opengl
{
Graphics::Graphics()
: quadIndices(nullptr)
, windowHasStencil(false)
: windowHasStencil(false)
, mainVAO(0)
{
gl = OpenGL();
@@ -87,8 +86,6 @@ Graphics::Graphics()
Graphics::~Graphics()
{
if (quadIndices)
delete quadIndices;
}
const char *Graphics::getName() const
@@ -226,13 +223,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
if (!Volatile::loadAll())
::printf("Could not reload all volatile objects.\n");
// 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(this);
createQuadIndexBuffer();
// Restore the graphics state.
restoreState(states.back());
@@ -354,6 +345,76 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
++drawCalls;
}
static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::Buffers &buffers, int vertexcount)
{
// TODO: Figure out a better way to avoid touching the same buffer multiple
// times, if multiple attributes share the buffer.
uint32 touchedbuffers = 0;
for (unsigned int i = 0; i < vertex::Attributes::MAX; i++)
{
if (!attributes.isEnabled(i))
continue;
auto &attrib = attributes.attribs[i];
uint32 bufferbit = 1u << attrib.bufferindex;
if ((touchedbuffers & bufferbit) == 0)
{
touchedbuffers |= bufferbit;
buffers.info[attrib.bufferindex].offset += attrib.stride * vertexcount;
}
}
}
void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, love::graphics::Texture *texture)
{
const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
const int MAX_QUADS_PER_DRAW = MAX_VERTICES_PER_DRAW / 4;
gl.prepareDraw();
gl.bindTextureToUnit(texture, 0, false);
gl.setCullMode(CULL_NONE);
gl.bindBuffer(BUFFER_INDEX, quadIndexBuffer->getHandle());
if (gl.isBaseVertexSupported())
{
gl.setVertexAttributes(attributes, buffers);
int basevertex = start;
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
{
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
glDrawElementsBaseVertex(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0), basevertex);
++drawCalls;
basevertex += quadcount * 4;
}
}
else
{
vertex::Buffers bufferscopy = buffers;
if (start > 0)
advanceVertexOffsets(attributes, bufferscopy, start * 4);
for (int quadindex = 0; quadindex < count; quadindex += MAX_QUADS_PER_DRAW)
{
gl.setVertexAttributes(attributes, bufferscopy);
int quadcount = std::min(MAX_QUADS_PER_DRAW, count - quadindex);
glDrawElements(GL_TRIANGLES, quadcount * 6, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));
++drawCalls;
if (count > MAX_QUADS_PER_DRAW)
advanceVertexOffsets(attributes, bufferscopy, quadcount * 4);
}
}
}
static void APIENTRY debugCB(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei /*len*/, const GLchar *msg, const GLvoid* /*usr*/)
{
// Human-readable strings for the debug info.
+1 -1
View File
@@ -71,6 +71,7 @@ public:
void draw(const DrawCommand &cmd) override;
void draw(const DrawIndexedCommand &cmd) override;
void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::Buffers &buffers, Texture *texture) override;
void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override;
void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) override;
@@ -127,7 +128,6 @@ private:
void setDebug(bool enable);
std::unordered_map<uint32, GLuint> framebufferObjects;
QuadIndices *quadIndices;
bool windowHasStencil;
GLuint mainVAO;
+35
View File
@@ -95,6 +95,7 @@ OpenGL::OpenGL()
: stats()
, contextInitialized(false)
, pixelShaderHighpSupported(false)
, baseVertexSupported(false)
, maxAnisotropy(1.0f)
, max2DTextureSize(0)
, max3DTextureSize(0)
@@ -375,6 +376,32 @@ void OpenGL::initOpenGLFunctions()
fp_glCompressedTexSubImage3D = fp_glCompressedTexSubImage3DOES;
fp_glFramebufferTexture3D = fp_glFramebufferTexture3DOES;
}
if (!GLAD_VERSION_3_2 && !GLAD_ES_VERSION_3_2 && !GLAD_ARB_draw_elements_base_vertex)
{
if (GLAD_OES_draw_elements_base_vertex)
{
fp_glDrawElementsBaseVertex = fp_glDrawElementsBaseVertexOES;
if (GLAD_ES_VERSION_3_0)
{
fp_glDrawRangeElementsBaseVertex = fp_glDrawRangeElementsBaseVertexOES;
fp_glDrawElementsInstancedBaseVertex = fp_glDrawElementsInstancedBaseVertexOES;
}
}
else if (GLAD_EXT_draw_elements_base_vertex)
{
fp_glDrawElementsBaseVertex = fp_glDrawElementsBaseVertexEXT;
if (GLAD_ES_VERSION_3_0)
{
fp_glDrawRangeElementsBaseVertex = fp_glDrawRangeElementsBaseVertexEXT;
fp_glDrawElementsInstancedBaseVertex = fp_glDrawElementsInstancedBaseVertexEXT;
}
}
}
}
void OpenGL::initMaxValues()
@@ -389,6 +416,9 @@ void OpenGL::initMaxValues()
else
pixelShaderHighpSupported = true;
baseVertexSupported = GLAD_VERSION_3_2 || GLAD_ES_VERSION_3_2 || GLAD_ARB_draw_elements_base_vertex
|| GLAD_OES_draw_elements_base_vertex || GLAD_EXT_draw_elements_base_vertex;
// We'll need this value to clamp anisotropy.
if (GLAD_EXT_texture_filter_anisotropic)
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropy);
@@ -1177,6 +1207,11 @@ bool OpenGL::isSamplerLODBiasSupported() const
return GLAD_VERSION_1_4;
}
bool OpenGL::isBaseVertexSupported() const
{
return baseVertexSupported;
}
int OpenGL::getMax2DTextureSize() const
{
return std::max(max2DTextureSize, 1);
+3
View File
@@ -334,6 +334,7 @@ public:
bool isInstancingSupported() const;
bool isDepthCompareSampleSupported() const;
bool isSamplerLODBiasSupported() const;
bool isBaseVertexSupported() const;
/**
* Returns the maximum supported width or height of a texture.
@@ -413,6 +414,8 @@ private:
bool contextInitialized;
bool pixelShaderHighpSupported;
bool baseVertexSupported;
float maxAnisotropy;
float maxLODBias;
int max2DTextureSize;