diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fa733584..92ff59d22 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -527,6 +527,7 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT src/modules/graphics/Polyline.h src/modules/graphics/Quad.cpp src/modules/graphics/Quad.h + src/modules/graphics/Resource.h src/modules/graphics/Shader.cpp src/modules/graphics/Shader.h src/modules/graphics/ShaderStage.cpp diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 5d6468d2d..b152771a6 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -1730,6 +1730,7 @@ FA0B7CCC1A95902C000E1D17 /* wrap_Window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wrap_Window.h; sourceTree = ""; }; FA0B7EF01A959D2C000E1D17 /* ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios.h; sourceTree = ""; }; FA0B7EF11A959D2C000E1D17 /* ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ios.mm; sourceTree = ""; }; + FA10DD7B1F9EC24E00E1FE3D /* Resource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Resource.h; sourceTree = ""; }; FA1557BF1CE90A2C00AFF582 /* tinyexr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tinyexr.h; sourceTree = ""; }; FA1557C11CE90BD200AFF582 /* EXRHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EXRHandler.cpp; sourceTree = ""; }; FA1557C21CE90BD200AFF582 /* EXRHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EXRHandler.h; sourceTree = ""; }; @@ -2725,6 +2726,7 @@ FA0B7B9C1A95902C000E1D17 /* Polyline.h */, FA0B7BBC1A95902C000E1D17 /* Quad.cpp */, FA0B7BBD1A95902C000E1D17 /* Quad.h */, + FA10DD7B1F9EC24E00E1FE3D /* Resource.h */, FA1BA0AF1E16FD0800AA2803 /* Shader.cpp */, FA1BA0B01E16FD0800AA2803 /* Shader.h */, FA3C5E401F8C368C0003C579 /* ShaderStage.cpp */, diff --git a/src/modules/graphics/Buffer.h b/src/modules/graphics/Buffer.h index 8df71bda5..d58df0aff 100644 --- a/src/modules/graphics/Buffer.h +++ b/src/modules/graphics/Buffer.h @@ -24,6 +24,7 @@ #include "common/config.h" #include "common/int.h" #include "vertex.h" +#include "Resource.h" // C #include @@ -38,7 +39,7 @@ class Graphics; /** * A block of GPU-owned memory. Currently meant for internal use. **/ -class Buffer +class Buffer : public Resource { public: @@ -91,11 +92,6 @@ public: **/ virtual void setMappedRangeModified(size_t offset, size_t size) = 0; - /** - * Gets the backend-specific handle for this Buffer. - **/ - virtual ptrdiff_t getHandle() const = 0; - /** * Fill a portion of the buffer with data and marks the range as modified. * diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index bb2ec67f0..e8153ecc7 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -323,6 +323,8 @@ void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::strin auto it = attachedAttributes.find(name); if (it != attachedAttributes.end()) oldattrib = it->second; + else if (attachedAttributes.size() + 1 > vertex::Attributes::MAX) + throw love::Exception("A maximum of %d attributes can be attached at once.", vertex::Attributes::MAX); newattrib.mesh = mesh; newattrib.enabled = oldattrib.mesh ? oldattrib.enabled : true; @@ -585,30 +587,48 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) if (Shader::current && texture.get()) Shader::current->checkMainTexture(texture); - uint32 enabledattribs = 0; - uint32 instancedattribs = 0; + vertex::Attributes attributes; + vertex::Buffers buffers; + + int activebuffers = 0; for (const auto &attrib : attachedAttributes) { if (!attrib.second.enabled) continue; - love::graphics::Mesh *mesh = attrib.second.mesh; - int location = mesh->bindAttributeToShaderInput(attrib.second.index, attrib.first); + Mesh *mesh = attrib.second.mesh; + int attributeindex = -1; - if (location >= 0) + // If the attribute is one of the LOVE-defined ones, use the constant + // attribute index for it, otherwise query the index from the shader. + VertexAttribID builtinattrib; + if (vertex::getConstant(attrib.first.c_str(), builtinattrib)) + attributeindex = (int) builtinattrib; + else if (Shader::current) + attributeindex = Shader::current->getVertexAttributeIndex(attrib.first); + + if (attributeindex >= 0) { - uint32 bit = 1u << (uint32) location; + // Make sure the buffer isn't mapped (sends data to GPU if needed.) + mesh->vbo->unmap(); - enabledattribs |= bit; + const auto &formats = mesh->getVertexFormat(); + const auto &format = formats[attrib.second.index]; - if (attrib.second.step == STEP_PER_INSTANCE) - instancedattribs |= bit; + uint16 offset = (uint16) mesh->getAttributeOffset(attrib.second.index); + uint16 stride = (uint16) mesh->getVertexStride(); + + attributes.set(attributeindex, format.type, format.components, offset, stride, activebuffers, attrib.second.step); + + // TODO: Ideally we want to reuse buffers with the same stride+step. + buffers.set(activebuffers, mesh->vbo, 0); + activebuffers++; } } // Not supported on all platforms or GL versions, I believe. - if (!(enabledattribs & ATTRIBFLAG_POS)) + if (!attributes.isEnabled(ATTRIB_POS)) throw love::Exception("Mesh must have an enabled VertexPosition attribute to be drawn."); bool useindexbuffer = useIndexBuffer && ibo != nullptr && indexCount > 0; @@ -643,7 +663,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) Graphics::TempTransform transform(gfx, m); if (count > 0) - drawInternal(start, count, instancecount, useindexbuffer, enabledattribs, instancedattribs); + drawInternal(start, count, instancecount, useindexbuffer, attributes, buffers); } } // graphics diff --git a/src/modules/graphics/Mesh.h b/src/modules/graphics/Mesh.h index 2cf103a34..b4e3361a4 100644 --- a/src/modules/graphics/Mesh.h +++ b/src/modules/graphics/Mesh.h @@ -167,8 +167,6 @@ public: void setDrawRange(); bool getDrawRange(int &start, int &count) const; - virtual int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) = 0; - // Implements Drawable. void draw(Graphics *gfx, const Matrix4 &m) override; @@ -178,6 +176,8 @@ public: protected: + friend class SpriteBatch; + struct AttachedAttribute { Mesh *mesh; @@ -190,7 +190,7 @@ protected: void calculateAttributeSizes(); size_t getAttributeOffset(size_t attribindex) const; - virtual void drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const = 0; + virtual void drawInternal(int start, int count, int instancecount, bool useindexbuffer, const vertex::Attributes &attributes, const vertex::Buffers &buffers) const = 0; std::vector vertexFormat; std::vector attributeSizes; diff --git a/src/modules/graphics/ParticleSystem.cpp b/src/modules/graphics/ParticleSystem.cpp index 7897e1358..77b71eb87 100644 --- a/src/modules/graphics/ParticleSystem.cpp +++ b/src/modules/graphics/ParticleSystem.cpp @@ -93,6 +93,7 @@ ParticleSystem::ParticleSystem(Graphics *gfx, Texture *texture, uint32 size) , offset(float(texture->getWidth())*0.5f, float(texture->getHeight())*0.5f) , defaultOffset(true) , relativeRotation(false) + , vertexAttributes(vertex::CommonFormat::XYf_STf_RGBAub, 0) , buffer(nullptr) , quadIndices(gfx, size) { @@ -154,6 +155,7 @@ ParticleSystem::ParticleSystem(const ParticleSystem &p) , colors(p.colors) , quads(p.quads) , relativeRotation(p.relativeRotation) + , vertexAttributes(p.vertexAttributes) , buffer(nullptr) , quadIndices(p.quadIndices) { @@ -1089,8 +1091,11 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m) buffer->unmap(); + vertex::Buffers vertexbuffers; + vertexbuffers.set(0, buffer, 0); + Graphics::TempTransform transform(gfx, m); - drawInternal(); + drawInternal(vertexAttributes, vertexbuffers); } bool ParticleSystem::getConstant(const char *in, AreaSpreadDistribution &out) diff --git a/src/modules/graphics/ParticleSystem.h b/src/modules/graphics/ParticleSystem.h index bc08185a7..4088a20ec 100644 --- a/src/modules/graphics/ParticleSystem.h +++ b/src/modules/graphics/ParticleSystem.h @@ -584,7 +584,7 @@ protected: int quadIndex; }; - virtual void drawInternal() const = 0; + virtual void drawInternal(const vertex::Attributes &attributes, const vertex::Buffers &buffers) const = 0; // Pointer to the beginning of the allocated memory. Particle *pMem; @@ -687,6 +687,7 @@ protected: bool relativeRotation; + const vertex::Attributes vertexAttributes; Buffer *buffer; // Vertex index buffer. diff --git a/src/modules/graphics/Resource.h b/src/modules/graphics/Resource.h new file mode 100644 index 000000000..8b4ac8f9c --- /dev/null +++ b/src/modules/graphics/Resource.h @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2006-2017 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#pragma once + +#include + +namespace love +{ +namespace graphics +{ + +class Resource +{ +public: + + virtual ~Resource() {} + virtual ptrdiff_t getHandle() const = 0; + +}; // Resource + +} // graphics +} // love diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index f2082f220..fed8b077d 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -25,6 +25,7 @@ #include "common/StringMap.h" #include "Texture.h" #include "ShaderStage.h" +#include "Resource.h" // STL #include @@ -45,7 +46,7 @@ namespace graphics class Graphics; // A GLSL shader -class Shader : public Object +class Shader : public Object, public Resource { public: @@ -185,8 +186,6 @@ public: void checkMainTextureType(TextureType textype, bool isDepthSampler) const; void checkMainTexture(Texture *texture) const; - virtual ptrdiff_t getHandle() const = 0; - static bool validate(ShaderStage *vertex, ShaderStage *pixel, std::string &err); static bool initialize(); diff --git a/src/modules/graphics/ShaderStage.h b/src/modules/graphics/ShaderStage.h index ff83b1001..6fcc0a2e5 100644 --- a/src/modules/graphics/ShaderStage.h +++ b/src/modules/graphics/ShaderStage.h @@ -23,6 +23,7 @@ #include "common/Object.h" #include "common/StringMap.h" #include "Volatile.h" +#include "Resource.h" #include #include @@ -39,7 +40,7 @@ namespace graphics class Graphics; -class ShaderStage : public love::Object, public Volatile +class ShaderStage : public love::Object, public Volatile, public Resource { public: @@ -57,8 +58,6 @@ public: const std::string &getSource() const { return source; } const std::string &getWarnings() const { return warnings; } glslang::TShader *getGLSLangShader() const { return glslangShader; } - - virtual ptrdiff_t getHandle() const = 0; static bool getConstant(const char *in, StageType &out); static bool getConstant(StageType in, const char *&out); diff --git a/src/modules/graphics/SpriteBatch.cpp b/src/modules/graphics/SpriteBatch.cpp index 79b851741..66fa7d5f0 100644 --- a/src/modules/graphics/SpriteBatch.cpp +++ b/src/modules/graphics/SpriteBatch.cpp @@ -334,14 +334,55 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) // Make sure the VBO isn't mapped when we draw (sends data to GPU if needed.) array_buf->unmap(); - CommonFormat format = vertex_format; + Attributes attributes; + Buffers buffers; - if (!color_active) { - if (format == CommonFormat::XYf_STPf_RGBAub) - format = CommonFormat::XYf_STPf; - else - format = CommonFormat::XYf_STf; + buffers.set(0, array_buf, 0); + attributes.setCommonFormat(vertex_format, 0); + + if (!color_active) + attributes.disable(ATTRIB_COLOR); + } + + int activebuffers = 1; + + for (const auto &it : attached_attributes) + { + Mesh *mesh = it.second.mesh.get(); + + // We have to do this check here as wll because setBufferSize can be + // called after attachAttribute. + if (mesh->getVertexCount() < (size_t) next * 4) + throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str()); + + int attributeindex = -1; + + // If the attribute is one of the LOVE-defined ones, use the constant + // attribute index for it, otherwise query the index from the shader. + VertexAttribID builtinattrib; + if (vertex::getConstant(it.first.c_str(), builtinattrib)) + attributeindex = (int) builtinattrib; + else if (Shader::current) + attributeindex = Shader::current->getVertexAttributeIndex(it.first); + + if (attributeindex >= 0) + { + // Make sure the buffer isn't mapped (sends data to GPU if needed.) + mesh->vbo->unmap(); + + const auto &formats = mesh->getVertexFormat(); + const auto &format = formats[it.second.index]; + + uint16 offset = (uint16) mesh->getAttributeOffset(it.second.index); + uint16 stride = (uint16) mesh->getVertexStride(); + + attributes.set(attributeindex, format.type, format.components, offset, stride, activebuffers); + + // TODO: Ideally we want to reuse buffers with the same stride+step. + buffers.set(activebuffers, mesh->vbo, 0); + activebuffers++; + } } int start = std::min(std::max(0, range_start), next - 1); @@ -358,7 +399,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) Graphics::TempTransform transform(gfx, m); if (count > 0) - drawInternal(format, indexbytestart, indexcount); + drawInternal(indexbytestart, indexcount, attributes, buffers); } } // graphics diff --git a/src/modules/graphics/SpriteBatch.h b/src/modules/graphics/SpriteBatch.h index 3a25c264f..36c0daad9 100644 --- a/src/modules/graphics/SpriteBatch.h +++ b/src/modules/graphics/SpriteBatch.h @@ -123,7 +123,7 @@ protected: **/ void setBufferSize(int newsize); - virtual void drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) = 0; + virtual void drawInternal(size_t indexbytestart, size_t indexcount, const vertex::Attributes &attributes, const vertex::Buffers &buffers) = 0; StrongRef texture; diff --git a/src/modules/graphics/StreamBuffer.h b/src/modules/graphics/StreamBuffer.h index dbcc2a49a..da872ba30 100644 --- a/src/modules/graphics/StreamBuffer.h +++ b/src/modules/graphics/StreamBuffer.h @@ -23,6 +23,7 @@ // LOVE #include "common/int.h" #include "vertex.h" +#include "Resource.h" // C #include @@ -32,7 +33,7 @@ namespace love namespace graphics { -class StreamBuffer +class StreamBuffer : public Resource { public: @@ -61,8 +62,6 @@ public: virtual void nextFrame() {} - virtual ptrdiff_t getHandle() const = 0; - protected: StreamBuffer(BufferType mode, size_t size); diff --git a/src/modules/graphics/Text.cpp b/src/modules/graphics/Text.cpp index 1c851d248..2dad5cd80 100644 --- a/src/modules/graphics/Text.cpp +++ b/src/modules/graphics/Text.cpp @@ -32,6 +32,7 @@ love::Type Text::type("Text", &Drawable::type); Text::Text(Graphics *gfx, Font *font, const std::vector &text) : font(font) + , vertexAttributes(Font::vertexFormat, 0) , vbo(nullptr) , quadIndices(gfx, 20) , vert_offset(0) @@ -68,6 +69,8 @@ void Text::uploadVertices(const std::vector &vertices, size_t delete vbo; vbo = new_vbo; + + vertexBuffers.set(0, vbo, 0); } if (vbo != nullptr && datasize > 0) diff --git a/src/modules/graphics/Text.h b/src/modules/graphics/Text.h index 7beeb553c..35b251a79 100644 --- a/src/modules/graphics/Text.h +++ b/src/modules/graphics/Text.h @@ -86,6 +86,10 @@ protected: virtual void drawInternal(const std::vector &commands) const = 0; StrongRef font; + + vertex::Attributes vertexAttributes; + vertex::Buffers vertexBuffers; + Buffer *vbo; QuadIndices quadIndices; diff --git a/src/modules/graphics/Texture.h b/src/modules/graphics/Texture.h index 328d0b2b2..64cf7b469 100644 --- a/src/modules/graphics/Texture.h +++ b/src/modules/graphics/Texture.h @@ -32,6 +32,7 @@ #include "Quad.h" #include "vertex.h" #include "depthstencil.h" +#include "Resource.h" // C #include @@ -56,7 +57,7 @@ enum TextureType * Base class for 2D textures. All textures can be drawn with Quads, have a * width and height, and have filter and wrap modes. **/ -class Texture : public Drawable +class Texture : public Drawable, public Resource { public: @@ -145,8 +146,6 @@ public: Quad *getQuad() const; - virtual ptrdiff_t getHandle() const = 0; - static bool validateFilter(const Filter &f, bool mipmapsAllowed); static int getMipmapCount(int w, int h); diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index 37076b0ab..177c5f96d 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -353,7 +353,9 @@ void Graphics::flushStreamDraws() OpenGL::TempDebugGroup debuggroup("Stream vertices flush and draw"); - uint32 attribs = 0; + Attributes attributes; + Buffers buffers; + size_t usedsizes[3] = {0, 0, 0}; for (int i = 0; i < 2; i++) @@ -361,26 +363,20 @@ void Graphics::flushStreamDraws() if (sbstate.formats[i] == CommonFormat::NONE) continue; + attributes.setCommonFormat(sbstate.formats[i], (uint8) i); + usedsizes[i] = getFormatStride(sbstate.formats[i]) * sbstate.vertexCount; - love::graphics::StreamBuffer *buffer = sbstate.vb[i]; - - gl.bindBuffer(BUFFER_VERTEX, (GLuint) buffer->getHandle()); - size_t offset = buffer->unmap(usedsizes[i]); - + size_t offset = sbstate.vb[i]->unmap(usedsizes[i]); + buffers.set(i, sbstate.vb[i], offset); sbstate.vbMap[i] = StreamBuffer::MapInfo(); - - gl.setVertexPointers(sbstate.formats[i], offset); - attribs |= getFormatFlags(sbstate.formats[i]); } - if (attribs == 0) + if (attributes.enablebits == 0) return; - GLenum glprimitivetype = OpenGL::getGLPrimitiveType(sbstate.primitiveMode); - Colorf nc = gl.getConstantColor(); - if (attribs & ATTRIBFLAG_COLOR) + if (attributes.isEnabled(ATTRIB_COLOR)) gl.setConstantColor(Colorf(1.0f, 1.0f, 1.0f, 1.0f)); pushIdentityTransform(); @@ -388,7 +384,9 @@ void Graphics::flushStreamDraws() gl.prepareDraw(); gl.bindTextureToUnit(sbstate.texture, 0, false); - gl.useVertexAttribArrays(attribs); + gl.setVertexAttributes(attributes, buffers); + + GLenum glprimitivetype = OpenGL::getGLPrimitiveType(sbstate.primitiveMode); if (sbstate.indexCount > 0) { @@ -415,7 +413,7 @@ void Graphics::flushStreamDraws() popTransform(); - if (attribs & ATTRIB_CONSTANTCOLOR) + if (attributes.isEnabled(ATTRIB_COLOR)) gl.setConstantColor(nc); streamBufferState.vertexCount = 0; diff --git a/src/modules/graphics/opengl/Mesh.cpp b/src/modules/graphics/opengl/Mesh.cpp index 2d89fb7bd..6159c542b 100644 --- a/src/modules/graphics/opengl/Mesh.cpp +++ b/src/modules/graphics/opengl/Mesh.cpp @@ -49,44 +49,11 @@ Mesh::~Mesh() { } -int Mesh::bindAttributeToShaderInput(int attributeindex, const std::string &inputname) -{ - const AttribFormat &format = vertexFormat[attributeindex]; - - GLint attriblocation = -1; - - // If the attribute is one of the LOVE-defined ones, use the constant - // attribute index for it, otherwise query the index from the shader. - VertexAttribID builtinattrib; - if (vertex::getConstant(inputname.c_str(), builtinattrib)) - attriblocation = (GLint) builtinattrib; - else if (Shader::current) - attriblocation = Shader::current->getVertexAttributeIndex(inputname); - - // The active shader might not use this vertex attribute name. - if (attriblocation < 0) - return attriblocation; - - // Make sure the buffer isn't mapped (sends data to GPU if needed.) - vbo->unmap(); - - gl.bindBuffer(BUFFER_VERTEX, (GLuint) vbo->getHandle()); - - GLboolean normalized = GL_FALSE; - GLenum datatype = OpenGL::getGLVertexDataType(format.type, normalized); - - const void *gloffset = BUFFER_OFFSET(getAttributeOffset(attributeindex)); - - glVertexAttribPointer(attriblocation, format.components, datatype, normalized, (GLsizei) vertexStride, gloffset); - - return attriblocation; -} - -void Mesh::drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const +void Mesh::drawInternal(int start, int count, int instancecount, bool useindexbuffer, const vertex::Attributes &attributes, const vertex::Buffers &buffers) const { OpenGL::TempDebugGroup debuggroup("Mesh draw"); - gl.useVertexAttribArrays(attribflags, instancedattribflags); + gl.setVertexAttributes(attributes, buffers); gl.bindTextureToUnit(texture, 0, false); gl.prepareDraw(); diff --git a/src/modules/graphics/opengl/Mesh.h b/src/modules/graphics/opengl/Mesh.h index 41d693974..28b30b417 100644 --- a/src/modules/graphics/opengl/Mesh.h +++ b/src/modules/graphics/opengl/Mesh.h @@ -37,14 +37,11 @@ public: Mesh(graphics::Graphics *gfx, const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage); Mesh(graphics::Graphics *gfx, const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage); - virtual ~Mesh(); - int bindAttributeToShaderInput(int attributeindex, const std::string &inputname) override; - protected: - void drawInternal(int start, int count, int instancecount, bool useindexbuffer, uint32 attribflags, uint32 instancedattribflags) const override; + void drawInternal(int start, int count, int instancecount, bool useindexbuffer, const vertex::Attributes &attributes, const vertex::Buffers &buffers) const override; }; // Mesh diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 6a73d236d..7b774b241 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -172,7 +172,7 @@ void OpenGL::setupContext() else state.instancedAttribArrays = 0; - useVertexAttribArrays(0, 0); + setVertexAttributes(vertex::Attributes(), vertex::Buffers()); // Get the current viewport. glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x); @@ -647,111 +647,52 @@ void OpenGL::drawElements(GLenum mode, GLsizei count, GLenum type, const void *i ++stats.drawCalls; } -void OpenGL::useVertexAttribArrays(uint32 arraybits, uint32 instancedbits) +void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const vertex::Buffers &buffers) { - uint32 diff = arraybits ^ state.enabledAttribArrays; - uint32 instancediff = instancedbits ^ state.instancedAttribArrays; + uint32 enablediff = attributes.enablebits ^ state.enabledAttribArrays; + uint32 instancediff = attributes.instancebits ^ state.instancedAttribArrays; - if (diff == 0 && instancediff == 0) - return; - - // Max 32 attributes. As of when this was written, no GL driver exposes more - // than 32. Lets hope that doesn't change... - for (uint32 i = 0; i < 32; i++) + for (uint32 i = 0; i < vertex::Attributes::MAX; i++) { uint32 bit = 1u << i; - if (diff & bit) + if (enablediff & bit) { - if (arraybits & bit) + if (attributes.enablebits & bit) glEnableVertexAttribArray(i); else glDisableVertexAttribArray(i); } if (instancediff & bit) - glVertexAttribDivisor(i, (instancedbits & bit) != 0 ? 1 : 0); + glVertexAttribDivisor(i, (attributes.instancebits & bit) != 0 ? 1 : 0); + + if (attributes.enablebits & bit) + { + const auto &attrib = attributes.attribs[i]; + const auto &bufferinfo = buffers.info[attrib.bufferindex]; + + GLboolean normalized = GL_FALSE; + GLenum gltype = getGLVertexDataType(attrib.type, normalized); + + const void *offsetpointer = BUFFER_OFFSET(bufferinfo.offset + attrib.offsetfromvertex); + + bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle()); + glVertexAttribPointer(i, attrib.components, gltype, normalized, attrib.stride, offsetpointer); + } } - state.enabledAttribArrays = arraybits; - state.instancedAttribArrays = instancedbits; + state.enabledAttribArrays = attributes.enablebits; + state.instancedAttribArrays = attributes.instancebits; // glDisableVertexAttribArray will make the constant value for a vertex // attribute undefined. We rely on the per-vertex color attribute being // white when no per-vertex color is used, so we set it here. // FIXME: Is there a better place to do this? - if ((diff & ATTRIBFLAG_COLOR) && !(arraybits & ATTRIBFLAG_COLOR)) + if ((enablediff & ATTRIBFLAG_COLOR) && !(attributes.enablebits & ATTRIBFLAG_COLOR)) glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f); } -void OpenGL::setVertexPointers(vertex::CommonFormat format, size_t stride, size_t offset) -{ - using namespace vertex; - - switch (format) - { - case CommonFormat::NONE: - break; - case CommonFormat::XYf: - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset)); - break; - case CommonFormat::XYZf: - glVertexAttribPointer(ATTRIB_POS, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset)); - break; - case CommonFormat::RGBAub: - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset)); - break; - case CommonFormat::STf_RGBAub: - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(STf_RGBAub, s))); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(STf_RGBAub, color.r))); - break; - case CommonFormat::STPf_RGBAub: - glVertexAttribPointer(ATTRIB_TEXCOORD, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(STPf_RGBAub, s))); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(STPf_RGBAub, color.r))); - break; - case CommonFormat::XYf_STf: - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf, x))); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf, s))); - break; - case CommonFormat::XYf_STPf: - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf, x))); - glVertexAttribPointer(ATTRIB_TEXCOORD, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf, s))); - break; - case CommonFormat::XYf_STf_RGBAub: - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, x))); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, s))); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STf_RGBAub, color.r))); - break; - case CommonFormat::XYf_STus_RGBAub: - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, x))); - glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, s))); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STus_RGBAub, color.r))); - break; - case CommonFormat::XYf_STPf_RGBAub: - glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf_RGBAub, x))); - glVertexAttribPointer(ATTRIB_TEXCOORD, 3, GL_FLOAT, GL_FALSE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf_RGBAub, s))); - glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, BUFFER_OFFSET(offset + offsetof(XYf_STPf_RGBAub, color.r))); - break; - } -} - -void OpenGL::setVertexPointers(vertex::CommonFormat format, size_t offset) -{ - setVertexPointers(format, getFormatStride(format), offset); -} - -void OpenGL::setVertexPointers(vertex::CommonFormat format, love::graphics::Buffer *buffer, size_t offset) -{ - bindBuffer(BUFFER_VERTEX, (GLuint) buffer->getHandle()); - setVertexPointers(format, offset); -} - -void OpenGL::setVertexPointers(vertex::CommonFormat format, love::graphics::Buffer *buffer, size_t stride, size_t offset) -{ - bindBuffer(BUFFER_VERTEX, (GLuint) buffer->getHandle()); - setVertexPointers(format, stride, offset); -} - void OpenGL::clearDepth(double value) { if (GLAD_ES_VERSION_2_0) diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 37ff4b40d..8c30272e8 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -46,6 +46,7 @@ namespace love namespace graphics { +class Resource; class Buffer; namespace opengl @@ -206,23 +207,7 @@ public: void drawArrays(GLenum mode, GLint first, GLsizei count, GLsizei instancecount = 1); void drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount = 1); - /** - * Sets the enabled vertex attribute arrays based on the specified attribute - * bits. Each bit in the uint32 represents an enabled attribute array index. - * For example, useVertexAttribArrays(1 << 0) will enable attribute index 0. - * See the VertexAttribFlags enum for the standard vertex attributes. - * This function *must* be used instead of glEnable/DisableVertexAttribArray. - **/ - void useVertexAttribArrays(uint32 arraybits, uint32 instancedbits = 0); - - /** - * Calls glVertexAttribPointer appropriately for each attribute used in the - * specified format. - **/ - void setVertexPointers(vertex::CommonFormat format, size_t offset); - void setVertexPointers(vertex::CommonFormat format, size_t stride, size_t offset); - void setVertexPointers(vertex::CommonFormat format, love::graphics::Buffer *buffer, size_t offset); - void setVertexPointers(vertex::CommonFormat format, love::graphics::Buffer *buffer, size_t stride, size_t offset); + void setVertexAttributes(const vertex::Attributes &attributes, const vertex::Buffers &buffers); /** * Wrapper for glClearDepth and glClearDepthf. diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index 8d65d8221..2616a0d5f 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -50,17 +50,14 @@ ParticleSystem *ParticleSystem::clone() return new ParticleSystem(*this); } -void ParticleSystem::drawInternal() const +void ParticleSystem::drawInternal(const vertex::Attributes &attributes, const vertex::Buffers &buffers) const { - using namespace vertex; - OpenGL::TempDebugGroup debuggroup("ParticleSystem draw"); gl.bindTextureToUnit(texture, 0, false); gl.prepareDraw(); - gl.useVertexAttribArrays(getFormatFlags(CommonFormat::XYf_STf_RGBAub)); - gl.setVertexPointers(CommonFormat::XYf_STf_RGBAub, buffer, 0); + gl.setVertexAttributes(attributes, buffers); GLsizei count = (GLsizei) quadIndices.getIndexCount(getCount()); GLenum gltype = OpenGL::getGLIndexDataType(quadIndices.getType()); diff --git a/src/modules/graphics/opengl/ParticleSystem.h b/src/modules/graphics/opengl/ParticleSystem.h index eafdbc310..411247018 100644 --- a/src/modules/graphics/opengl/ParticleSystem.h +++ b/src/modules/graphics/opengl/ParticleSystem.h @@ -47,7 +47,7 @@ public: private: - void drawInternal() const override; + void drawInternal(const vertex::Attributes &attributes, const vertex::Buffers &buffers) const override; }; // ParticleSystem diff --git a/src/modules/graphics/opengl/SpriteBatch.cpp b/src/modules/graphics/opengl/SpriteBatch.cpp index 14accb645..5e7525510 100644 --- a/src/modules/graphics/opengl/SpriteBatch.cpp +++ b/src/modules/graphics/opengl/SpriteBatch.cpp @@ -51,32 +51,11 @@ SpriteBatch::~SpriteBatch() { } -void SpriteBatch::drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) +void SpriteBatch::drawInternal(size_t indexbytestart, size_t indexcount, const vertex::Attributes &attributes, const vertex::Buffers &buffers) { OpenGL::TempDebugGroup debuggroup("SpriteBatch draw"); - uint32 enabledattribs = getFormatFlags(format); - - // We want attached attributes to override local attributes, so we should - // call this before binding attached attributes. - gl.setVertexPointers(format, array_buf, vertex_stride, 0); - - for (const auto &it : attached_attributes) - { - Mesh *mesh = it.second.mesh.get(); - - // We have to do this check here as wll because setBufferSize can be - // called after attachAttribute. - if (mesh->getVertexCount() < (size_t) next * 4) - throw love::Exception("Mesh with attribute '%s' attached to this SpriteBatch has too few vertices", it.first.c_str()); - - int location = mesh->bindAttributeToShaderInput(it.second.index, it.first); - - if (location >= 0) - enabledattribs |= 1u << (uint32) location; - } - - gl.useVertexAttribArrays(enabledattribs); + gl.setVertexAttributes(attributes, buffers); gl.bindTextureToUnit(texture, 0, false); gl.prepareDraw(); diff --git a/src/modules/graphics/opengl/SpriteBatch.h b/src/modules/graphics/opengl/SpriteBatch.h index 35e7bb8a6..979c64dc9 100644 --- a/src/modules/graphics/opengl/SpriteBatch.h +++ b/src/modules/graphics/opengl/SpriteBatch.h @@ -39,7 +39,7 @@ public: protected: - void drawInternal(vertex::CommonFormat format, size_t indexbytestart, size_t indexcount) override; + void drawInternal(size_t indexbytestart, size_t indexcount, const vertex::Attributes &attributes, const vertex::Buffers &buffers) override; }; // SpriteBatch diff --git a/src/modules/graphics/opengl/Text.cpp b/src/modules/graphics/opengl/Text.cpp index 9f6f2b1dd..cacca30f7 100644 --- a/src/modules/graphics/opengl/Text.cpp +++ b/src/modules/graphics/opengl/Text.cpp @@ -45,9 +45,7 @@ void Text::drawInternal(const std::vector &commands) const OpenGL::TempDebugGroup debuggroup("Text object draw"); gl.prepareDraw(); - - gl.setVertexPointers(Font::vertexFormat, vbo, 0); - gl.useVertexAttribArrays(vertex::getFormatFlags(Font::vertexFormat)); + gl.setVertexAttributes(vertexAttributes, vertexBuffers); const GLenum gltype = OpenGL::getGLIndexDataType(quadIndices.getType()); const size_t elemsize = quadIndices.getElementSize(); diff --git a/src/modules/graphics/vertex.cpp b/src/modules/graphics/vertex.cpp index 52b090d49..665406ffa 100644 --- a/src/modules/graphics/vertex.cpp +++ b/src/modules/graphics/vertex.cpp @@ -37,6 +37,8 @@ static_assert(sizeof(XYf_STf_RGBAub) == sizeof(float)*2 + sizeof(float)*2 + size static_assert(sizeof(XYf_STus_RGBAub) == sizeof(float)*2 + sizeof(uint16)*2 + sizeof(Color), "sizeof(XYf_STus_RGBAub) incorrect!"); static_assert(sizeof(XYf_STPf_RGBAub) == sizeof(float)*2 + sizeof(float)*3 + sizeof(Color), "sizeof(XYf_STPf_RGBAub) incorrect!"); +static_assert(sizeof(AttributeInfo) == sizeof(uint64), "sizeof(AttributeInfo) incorrect!"); + size_t getFormatStride(CommonFormat format) { switch (format) @@ -222,6 +224,57 @@ void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, fillIndicesT(mode, vertexStart, vertexCount, indices); } +void Attributes::setCommonFormat(CommonFormat format, uint8 bufferindex) +{ + uint16 stride = (uint16) getFormatStride(format); + + switch (format) + { + case CommonFormat::NONE: + break; + case CommonFormat::XYf: + set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex); + break; + case CommonFormat::XYZf: + set(ATTRIB_POS, DATA_FLOAT, 3, 0, stride, bufferindex); + break; + case CommonFormat::RGBAub: + set(ATTRIB_COLOR, DATA_UNORM8, 4, 0, stride, bufferindex); + break; + case CommonFormat::STf_RGBAub: + set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, 0, stride, bufferindex); + set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2), stride, bufferindex); + break; + case CommonFormat::STPf_RGBAub: + set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, 0, stride, bufferindex); + set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 3), stride, bufferindex); + break; + case CommonFormat::XYf_STf: + set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex); + set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), stride, bufferindex); + break; + case CommonFormat::XYf_STPf: + set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex); + set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), stride, bufferindex); + break; + case CommonFormat::XYf_STf_RGBAub: + set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex); + set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), stride, bufferindex); + set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 4), stride, bufferindex); + break; + case CommonFormat::XYf_STus_RGBAub: + set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex); + set(ATTRIB_TEXCOORD, DATA_UNORM16, 2, uint16(sizeof(float) * 2), stride, bufferindex); + set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), stride, bufferindex); + break; + case CommonFormat::XYf_STPf_RGBAub: + set(ATTRIB_POS, DATA_FLOAT, 2, 0, stride, bufferindex); + set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), stride, bufferindex); + set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 5), stride, bufferindex); + break; + } +} + static StringMap::Entry attribNameEntries[] = { { "VertexPosition", ATTRIB_POS }, diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index 613284f2c..505278fc7 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -34,6 +34,8 @@ namespace love namespace graphics { +class Resource; + // Vertex attribute indices used in shaders by LOVE. The values map to OpenGL // generic vertex attribute indices. enum VertexAttribID @@ -172,6 +174,119 @@ struct XYf_STPf_RGBAub Color color; }; +struct Buffers +{ + static const unsigned int MAX = 32; + + uint32 usebits = 0; + + struct + { + Resource *buffer; + size_t offset; + } info[MAX]; + + void set(unsigned int index, Resource *r, size_t offset) + { + usebits |= (1u << index); + info[index] = {r, offset}; + } + + void disable(unsigned int index) { usebits &= (1u << index); } + void clear() { usebits = 0; } +}; + +struct AttributeInfo +{ + uint8 bufferindex; + DataType type : 8; + uint16 components; + uint16 offsetfromvertex; + uint16 stride; +}; + +struct Attributes +{ + static const unsigned int MAX = 32; + + uint32 enablebits = 0; + uint32 instancebits = 0; + + union + { + AttributeInfo attribs[MAX]; + uint64 data[MAX]; + }; + + Attributes() {} + Attributes(CommonFormat format, uint8 bufferindex) + { + setCommonFormat(format, bufferindex); + } + + void set(unsigned int index, DataType type, uint16 components, uint16 offsetfromvertex, uint16 stride, uint8 bufferindex, AttributeStep step = STEP_PER_VERTEX) + { + uint32 bit = (1u << index); + + enablebits |= bit; + + if (step == STEP_PER_INSTANCE) + instancebits |= bit; + else + instancebits &= ~bit; + + attribs[index].bufferindex = bufferindex; + attribs[index].type = type; + attribs[index].components = components; + attribs[index].offsetfromvertex = offsetfromvertex; + attribs[index].stride = stride; + } + + void disable(unsigned int index) + { + enablebits &= ~(1u << index); + } + + void clear() + { + enablebits = 0; + } + + bool isEnabled(unsigned int index) const + { + return (enablebits & (1u << index)) != 0; + } + + AttributeStep getStep(unsigned int index) const + { + return (instancebits & (1u << index)) != 0 ? STEP_PER_INSTANCE : STEP_PER_VERTEX; + } + + void setCommonFormat(CommonFormat format, uint8 bufferindex); + + bool operator == (const Attributes &other) const + { + if (enablebits != other.enablebits) + return false; + + uint32 instancediff = instancebits ^ other.instancebits; + + for (unsigned int i = 0; i < MAX; i++) + { + if (isEnabled(i)) + { + if (instancediff & (1u << i)) + return false; + + if (data[i] != other.data[i]) + return false; + } + } + + return true; + } +}; + size_t getFormatStride(CommonFormat format); uint32 getFormatFlags(CommonFormat format);