diff --git a/CMakeLists.txt b/CMakeLists.txt index 00341b225..541b0ea14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -527,6 +527,8 @@ set(LOVE_SRC_MODULE_GRAPHICS_ROOT src/modules/graphics/Video.h src/modules/graphics/Volatile.cpp src/modules/graphics/Volatile.h + src/modules/graphics/wrap_Buffer.cpp + src/modules/graphics/wrap_Buffer.h src/modules/graphics/wrap_Font.cpp src/modules/graphics/wrap_Font.h src/modules/graphics/wrap_Graphics.cpp diff --git a/platform/xcode/liblove.xcodeproj/project.pbxproj b/platform/xcode/liblove.xcodeproj/project.pbxproj index 68ed3dbc0..ce4d481ca 100644 --- a/platform/xcode/liblove.xcodeproj/project.pbxproj +++ b/platform/xcode/liblove.xcodeproj/project.pbxproj @@ -775,6 +775,9 @@ FA15DFB01F9B8D6A0042AB22 /* wrap_Data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA6A2B651F5F7B6B0074C308 /* wrap_Data.cpp */; }; FA15DFB11F9B8D820042AB22 /* OggDemuxer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAA54AC91F91660400A8FA7B /* OggDemuxer.cpp */; }; FA15DFB21F9B8D840042AB22 /* TheoraVideoStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FAA54AC81F91660400A8FA7B /* TheoraVideoStream.cpp */; }; + FA18CEC523D3AE6700263725 /* wrap_Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEC323D3AE6700263725 /* wrap_Buffer.cpp */; }; + FA18CEC623D3AE6800263725 /* wrap_Buffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEC323D3AE6700263725 /* wrap_Buffer.cpp */; }; + FA18CEC723D3AE6800263725 /* wrap_Buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = FA18CEC423D3AE6700263725 /* wrap_Buffer.h */; }; FA1BA09D1E16CFCE00AA2803 /* Font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA1BA09B1E16CFCE00AA2803 /* Font.cpp */; }; FA1BA09E1E16CFCE00AA2803 /* Font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA1BA09B1E16CFCE00AA2803 /* Font.cpp */; }; FA1BA09F1E16CFCE00AA2803 /* Font.h in Headers */ = {isa = PBXBuildFile; fileRef = FA1BA09C1E16CFCE00AA2803 /* Font.h */; }; @@ -1782,6 +1785,8 @@ 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 = ""; }; FA15DFAB1F9B8C850042AB22 /* StringMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringMap.cpp; sourceTree = ""; }; + FA18CEC323D3AE6700263725 /* wrap_Buffer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Buffer.cpp; sourceTree = ""; }; + FA18CEC423D3AE6700263725 /* wrap_Buffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = wrap_Buffer.h; sourceTree = ""; }; FA1BA09B1E16CFCE00AA2803 /* Font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Font.cpp; sourceTree = ""; }; FA1BA09C1E16CFCE00AA2803 /* Font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Font.h; sourceTree = ""; }; FA1BA0A01E16D97500AA2803 /* wrap_Font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wrap_Font.cpp; sourceTree = ""; }; @@ -2849,6 +2854,8 @@ FADF54061E3D78F700012CC0 /* Video.h */, FA0B7BC01A95902C000E1D17 /* Volatile.cpp */, FA0B7BC11A95902C000E1D17 /* Volatile.h */, + FA18CEC323D3AE6700263725 /* wrap_Buffer.cpp */, + FA18CEC423D3AE6700263725 /* wrap_Buffer.h */, FA1BA0A01E16D97500AA2803 /* wrap_Font.cpp */, FA1BA0A11E16D97500AA2803 /* wrap_Font.h */, FADF54391E3DAFF700012CC0 /* wrap_Graphics.cpp */, @@ -3929,6 +3936,7 @@ FA0B7A631A958EA3000E1D17 /* b2ContactManager.h in Headers */, FA0B7E771A95902C000E1D17 /* wrap_WeldJoint.h in Headers */, FA0B7A911A958EA3000E1D17 /* b2FrictionJoint.h in Headers */, + FA18CEC723D3AE6800263725 /* wrap_Buffer.h in Headers */, FA0B7E291A95902C000E1D17 /* PulleyJoint.h in Headers */, FA6BDE5C1F31725300786805 /* Color.h in Headers */, FA0B7E231A95902C000E1D17 /* PolygonShape.h in Headers */, @@ -4497,6 +4505,7 @@ FAF6C9E023C2DE2900D7B5BC /* SpvTools.cpp in Sources */, FAE64A8F2071364200BC7981 /* physfs_platform_unix.c in Sources */, FA0B7A681A958EA3000E1D17 /* b2Island.cpp in Sources */, + FA18CEC623D3AE6800263725 /* wrap_Buffer.cpp in Sources */, FA0B7E2B1A95902C000E1D17 /* RevoluteJoint.cpp in Sources */, FA0B7B291A958EA3000E1D17 /* simplexnoise1234.cpp in Sources */, FA0B7D261A95902C000E1D17 /* wrap_Font.cpp in Sources */, @@ -4748,6 +4757,7 @@ FAF140A91E20934C00F898D2 /* SymbolTable.cpp in Sources */, FA0B7E181A95902C000E1D17 /* MotorJoint.cpp in Sources */, FA0B7E4E1A95902C000E1D17 /* wrap_Fixture.cpp in Sources */, + FA18CEC523D3AE6700263725 /* wrap_Buffer.cpp in Sources */, FAF6C9F423C2DE2900D7B5BC /* Logger.cpp in Sources */, FA0B7EBE1A95902C000E1D17 /* Thread.cpp in Sources */, FAC7CD8F1FE35E95006A60C7 /* physfs_platform_posix.c in Sources */, diff --git a/src/common/StringMap.h b/src/common/StringMap.h index b53b46487..4e6dc25de 100644 --- a/src/common/StringMap.h +++ b/src/common/StringMap.h @@ -182,6 +182,21 @@ private: }; // StringMap +#define STRINGMAP_DECLARE(type) \ +bool getConstant(const char *in, type &out); \ +bool getConstant(type in, const char *&out); \ +std::vector getConstants(type); \ + +#define STRINGMAP_BEGIN(type, count, name) \ +static StringMap::Entry name##Entries[] = + +#define STRINGMAP_END(type, count, name) \ +; \ +static StringMap name##s(name##Entries, sizeof(name##Entries)); \ +bool getConstant(const char *in, type &out) { return name##s.find(in, out); } \ +bool getConstant(type in, const char *&out) { return name##s.find(in, out); } \ +std::vector getConstants(type) { return name##s.getNames(); } + } // love #endif // LOVE_STRING_MAP_H diff --git a/src/modules/graphics/Buffer.cpp b/src/modules/graphics/Buffer.cpp index f67e46d54..6f4996d10 100644 --- a/src/modules/graphics/Buffer.cpp +++ b/src/modules/graphics/Buffer.cpp @@ -19,24 +19,175 @@ **/ #include "Buffer.h" +#include "Graphics.h" namespace love { namespace graphics { -Buffer::Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags) - : size(size) - , type(type) - , usage(usage) - , map_flags(mapflags) - , is_mapped(false) +love::Type Buffer::type("GraphicsBuffer", &Object::type); + +Buffer::Buffer(Graphics *gfx, const Settings &settings, const std::vector &bufferformat, size_t size, size_t arraylength) + : arrayLength(0) + , arrayStride(0) + , size(size) + , typeFlags(settings.typeFlags) + , usage(settings.usage) + , mapFlags(settings.mapFlags) + , mapped(false) { + if (size == 0 && arraylength == 0) + throw love::Exception("Size or array length must be specified."); + + if (bufferformat.size() == 0) + throw love::Exception("Data format must contain values."); + + bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3]; + + bool indexbuffer = settings.typeFlags & TYPEFLAG_INDEX; + bool vertexbuffer = settings.typeFlags & TYPEFLAG_VERTEX; + + if (!indexbuffer && !vertexbuffer) + throw love::Exception("Buffer must be created with at least one buffer type (index or vertex)."); + + size_t offset = 0; + size_t stride = 0; + + for (const DataDeclaration &decl : bufferformat) + { + DataMember member(decl); + + DataFormat format = member.decl.format; + const DataFormatInfo &info = member.info; + + if (indexbuffer) + { + if (format != DATAFORMAT_UINT16 && format != DATAFORMAT_UINT32) + throw love::Exception("Index buffers only support uint16 and uint32 data types."); + + if (bufferformat.size() > 1) + throw love::Exception("Index buffers only support a single value per element."); + } + + if (vertexbuffer) + { + if (decl.arrayLength > 0) + throw love::Exception("Arrays are not supported in vertex buffers."); + + if (info.isMatrix) + throw love::Exception("Matrix types are not supported in vertex buffers."); + + if (info.baseType == DATA_BASETYPE_BOOL) + throw love::Exception("Bool types are not supported in vertex buffers."); + + if ((info.baseType == DATA_BASETYPE_INT || info.baseType == DATA_BASETYPE_UINT) && !supportsGLSL3) + throw love::Exception("Integer vertex attribute data types require GLSL 3 support."); + } + + // TODO: alignment + member.offset = offset; + member.size = member.info.size; + + offset += member.size; + + dataMembers.push_back(member); + } + + stride = offset; + + if (size != 0) + { + size_t remainder = size % stride; + if (remainder > 0) + size += stride - remainder; + arraylength = size / stride; + } + else + { + size = arraylength * stride; + } + + this->arrayStride = stride; + this->arrayLength = arraylength; + this->size = size; } Buffer::~Buffer() { } +int Buffer::getDataMemberIndex(const std::string &name) const +{ + for (size_t i = 0; i < dataMembers.size(); i++) + { + if (dataMembers[i].decl.name == name) + return (int) i; + } + + return -1; +} + +std::vector Buffer::getCommonFormatDeclaration(CommonFormat format) +{ + switch (format) + { + case CommonFormat::NONE: + return {}; + case CommonFormat::XYf: + return { + { getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 } + }; + case CommonFormat::XYZf: + return { + { getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC3 } + }; + case CommonFormat::RGBAub: + return { + { getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 } + }; + case CommonFormat::STf_RGBAub: + return { + { getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 }, + { getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 }, + }; + case CommonFormat::STPf_RGBAub: + return { + { getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3 }, + { getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 }, + }; + case CommonFormat::XYf_STf: + return { + { getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 }, + { getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 }, + }; + case CommonFormat::XYf_STPf: + return { + { getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 }, + { getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC3 }, + }; + case CommonFormat::XYf_STf_RGBAub: + return { + { getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 }, + { getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 }, + { getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 }, + }; + case CommonFormat::XYf_STus_RGBAub: + return { + { getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 }, + { getConstant(ATTRIB_TEXCOORD), DATAFORMAT_UNORM16_VEC2 }, + { getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 }, + }; + case CommonFormat::XYf_STPf_RGBAub: + return { + { getConstant(ATTRIB_POS), DATAFORMAT_FLOAT_VEC2 }, + { getConstant(ATTRIB_TEXCOORD), DATAFORMAT_FLOAT_VEC2 }, + { getConstant(ATTRIB_COLOR), DATAFORMAT_UNORM8_VEC4 }, + }; + } + + return {}; +} + } // graphics } // love diff --git a/src/modules/graphics/Buffer.h b/src/modules/graphics/Buffer.h index 8d6df1932..6411f9064 100644 --- a/src/modules/graphics/Buffer.h +++ b/src/modules/graphics/Buffer.h @@ -23,40 +23,101 @@ // LOVE #include "common/config.h" #include "common/int.h" +#include "common/Object.h" #include "vertex.h" #include "Resource.h" // C #include +#include +#include namespace love { namespace graphics { +class Graphics; + /** - * A block of GPU-owned memory. Currently meant for internal use. + * A block of GPU-owned memory. **/ -class Buffer : public Resource +class Buffer : public love::Object, public Resource { public: + static love::Type type; + enum MapFlags { + MAP_NONE = 0, MAP_EXPLICIT_RANGE_MODIFY = (1 << 0), // see setMappedRangeModified. MAP_READ = (1 << 1), }; - Buffer(size_t size, BufferType type, vertex::Usage usage, uint32 mapflags); + enum TypeFlags + { + TYPEFLAG_NONE = 0, + TYPEFLAG_VERTEX = 1 << BUFFERTYPE_VERTEX, + TYPEFLAG_INDEX = 1 << BUFFERTYPE_INDEX, + }; + + struct DataDeclaration + { + std::string name; + DataFormat format; + int arrayLength; + + DataDeclaration(const std::string &name, DataFormat format, int arrayLength = 0) + : name(name) + , format(format) + , arrayLength(arrayLength) + {} + }; + + struct DataMember + { + DataDeclaration decl; + DataFormatInfo info; + size_t offset; + size_t size; + + DataMember(const DataDeclaration &decl) + : decl(decl) + , info(getDataFormatInfo(decl.format)) + , offset(0) + , size(0) + {} + }; + + struct Settings + { + TypeFlags typeFlags; + MapFlags mapFlags; + BufferUsage usage; + + Settings(uint32 typeflags, uint32 mapflags, BufferUsage usage) + : typeFlags((TypeFlags)typeflags) + , mapFlags((MapFlags)mapflags) + , usage(usage) + {} + }; + + Buffer(Graphics *gfx, const Settings &settings, const std::vector &format, size_t size, size_t arraylength); virtual ~Buffer(); size_t getSize() const { return size; } + TypeFlags getTypeFlags() const { return typeFlags; } + BufferUsage getUsage() const { return usage; } + bool isMapped() const { return mapped; } + uint32 getMapFlags() const { return mapFlags; } - BufferType getType() const { return type; } - - vertex::Usage getUsage() const { return usage; } - - bool isMapped() const { return is_mapped; } + size_t getArrayLength() const { return arrayLength; } + size_t getArrayStride() const { return arrayStride; } + const std::vector &getDataMembers() const { return dataMembers; } + const DataMember &getDataMember(int index) const { return dataMembers[index]; } + size_t getMemberOffset(int index) const { return dataMembers[index].offset; } + int getDataMemberIndex(const std::string &name) const; /** * Map the Buffer to client memory. @@ -92,58 +153,48 @@ public: **/ virtual void copyTo(size_t offset, size_t size, Buffer *other, size_t otheroffset) = 0; - uint32 getMapFlags() const { return map_flags; } + static std::vector getCommonFormatDeclaration(CommonFormat format); class Mapper { public: - /** - * Memory-maps a Buffer. - */ Mapper(Buffer &buffer) - : buf(buffer) + : buffer(buffer) { - elems = buf.map(); + data = buffer.map(); } - /** - * unmaps the buffer - */ ~Mapper() { - buf.unmap(); + if (buffer.getMapFlags() & MAP_EXPLICIT_RANGE_MODIFY) + buffer.setMappedRangeModified(0, buffer.getSize()); + buffer.unmap(); } - /** - * Get pointer to memory mapped region - */ - void *get() - { - return elems; - } - - private: - - Buffer &buf; - void *elems; + Buffer &buffer; + void *data; }; // Mapper protected: + std::vector dataMembers; + size_t arrayLength; + size_t arrayStride; + // The size of the buffer, in bytes. size_t size; // The type of the buffer object. - BufferType type; + TypeFlags typeFlags; // Usage hint. GL_[DYNAMIC, STATIC, STREAM]_DRAW. - vertex::Usage usage; + BufferUsage usage; - uint32 map_flags; + uint32 mapFlags; - bool is_mapped; + bool mapped; }; // Buffer diff --git a/src/modules/graphics/Font.cpp b/src/modules/graphics/Font.cpp index 099e74a05..a3ae3082a 100644 --- a/src/modules/graphics/Font.cpp +++ b/src/modules/graphics/Font.cpp @@ -45,7 +45,7 @@ static inline uint16 normToUint16(double n) love::Type Font::type("Font", &Object::type); int Font::fontCount = 0; -const vertex::CommonFormat Font::vertexFormat = vertex::CommonFormat::XYf_STus_RGBAub; +const CommonFormat Font::vertexFormat = CommonFormat::XYf_STus_RGBAub; Font::Font(love::font::Rasterizer *r, const SamplerState &s) : rasterizers({r}) @@ -647,7 +647,7 @@ void Font::printv(graphics::Graphics *gfx, const Matrix4 &t, const std::vector Codepoints; - typedef vertex::XYf_STus_RGBAub GlyphVertex; + typedef XYf_STus_RGBAub GlyphVertex; - static const vertex::CommonFormat vertexFormat; + static const CommonFormat vertexFormat; enum AlignMode { diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 4ff2e7d1e..7fbe0a7b5 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -171,10 +171,12 @@ void Graphics::createQuadIndexBuffer() return; size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6; - quadIndexBuffer = newBuffer(size, nullptr, BUFFER_INDEX, vertex::USAGE_STATIC, 0); + + Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, 0, BUFFERUSAGE_STATIC); + quadIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0); Buffer::Mapper map(*quadIndexBuffer); - vertex::fillIndices(vertex::TriangleIndexMode::QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.get()); + fillIndices(TRIANGLEINDEX_QUADS, 0, LOVE_UINT16_MAX, (uint16 *) map.data); } Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh) @@ -202,7 +204,7 @@ Video *Graphics::newVideo(love::video::VideoStream *stream, float dpiscale) return new Video(this, stream, dpiscale); } -love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, vertex::Usage usage) +love::graphics::SpriteBatch *Graphics::newSpriteBatch(Texture *texture, int size, BufferUsage usage) { return new SpriteBatch(this, texture, size, usage); } @@ -287,26 +289,27 @@ Shader *Graphics::newShader(const std::vector &stagessource) return newShaderInternal(stages[ShaderStage::STAGE_VERTEX], stages[ShaderStage::STAGE_PIXEL]); } -Mesh *Graphics::newMesh(const std::vector &vertices, PrimitiveType drawmode, vertex::Usage usage) +Buffer *Graphics::newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength) { - return newMesh(Mesh::getDefaultVertexFormat(), &vertices[0], vertices.size() * sizeof(Vertex), drawmode, usage); + std::vector dataformat = {{"", format, 0}}; + return newBuffer(settings, dataformat, data, size, arraylength); } -Mesh *Graphics::newMesh(int vertexcount, PrimitiveType drawmode, vertex::Usage usage) -{ - return newMesh(Mesh::getDefaultVertexFormat(), vertexcount, drawmode, usage); -} - -love::graphics::Mesh *Graphics::newMesh(const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage) +Mesh *Graphics::newMesh(const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage) { return new Mesh(this, vertexformat, vertexcount, drawmode, usage); } -love::graphics::Mesh *Graphics::newMesh(const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage) +Mesh *Graphics::newMesh(const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage) { return new Mesh(this, vertexformat, data, datasize, drawmode, usage); } +Mesh *Graphics::newMesh(const std::vector &attributes, PrimitiveType drawmode) +{ + return new Mesh(attributes, drawmode); +} + love::graphics::Text *Graphics::newText(graphics::Font *font, const std::vector &text) { return new Text(font, text); @@ -944,7 +947,7 @@ CullMode Graphics::getMeshCullMode() const return states.back().meshCullMode; } -vertex::Winding Graphics::getFrontFaceWinding() const +Winding Graphics::getFrontFaceWinding() const { return states.back().winding; } @@ -1033,8 +1036,6 @@ void Graphics::captureScreenshot(const ScreenshotInfo &info) Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawCommand &cmd) { - using namespace vertex; - BatchedDrawState &state = batchedDrawState; bool shouldflush = false; @@ -1042,7 +1043,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman if (cmd.primitiveMode != state.primitiveMode || cmd.formats[0] != state.formats[0] || cmd.formats[1] != state.formats[1] - || ((cmd.indexMode != TriangleIndexMode::NONE) != (state.indexCount > 0)) + || ((cmd.indexMode != TRIANGLEINDEX_NONE) != (state.indexCount > 0)) || cmd.texture != state.texture || cmd.standardShaderType != state.standardShaderType) { @@ -1052,7 +1053,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman int totalvertices = state.vertexCount + cmd.vertexCount; // We only support uint16 index buffers for now. - if (totalvertices > LOVE_UINT16_MAX && cmd.indexMode != TriangleIndexMode::NONE) + if (totalvertices > LOVE_UINT16_MAX && cmd.indexMode != TRIANGLEINDEX_NONE) shouldflush = true; int reqIndexCount = getIndexCount(cmd.indexMode, cmd.vertexCount); @@ -1081,7 +1082,7 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman newdatasizes[i] = stride * cmd.vertexCount; } - if (cmd.indexMode != TriangleIndexMode::NONE) + if (cmd.indexMode != TRIANGLEINDEX_NONE) { size_t datasize = (state.indexCount + reqIndexCount) * sizeof(uint16); @@ -1119,18 +1120,18 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman if (state.vb[i]->getSize() < buffersizes[i]) { delete state.vb[i]; - state.vb[i] = newStreamBuffer(BUFFER_VERTEX, buffersizes[i]); + state.vb[i] = newStreamBuffer(BUFFERTYPE_VERTEX, buffersizes[i]); } } if (state.indexBuffer->getSize() < buffersizes[2]) { delete state.indexBuffer; - state.indexBuffer = newStreamBuffer(BUFFER_INDEX, buffersizes[2]); + state.indexBuffer = newStreamBuffer(BUFFERTYPE_INDEX, buffersizes[2]); } } - if (cmd.indexMode != TriangleIndexMode::NONE) + if (cmd.indexMode != TRIANGLEINDEX_NONE) { if (state.indexBufferMap.data == nullptr) state.indexBufferMap = state.indexBuffer->map(reqIndexSize); @@ -1167,14 +1168,12 @@ Graphics::BatchedVertexData Graphics::requestBatchedDraw(const BatchedDrawComman void Graphics::flushBatchedDraws() { - using namespace vertex; - auto &sbstate = batchedDrawState; if (sbstate.vertexCount == 0 && sbstate.indexCount == 0) return; - Attributes attributes; + VertexAttributes attributes; BufferBindings buffers; size_t usedsizes[3] = {0, 0, 0}; @@ -1317,8 +1316,8 @@ void Graphics::points(const Vector2 *positions, const Colorf *colors, size_t num BatchedDrawCommand cmd; cmd.primitiveMode = PRIMITIVE_POINTS; - cmd.formats[0] = vertex::getSinglePositionFormat(is2D); - cmd.formats[1] = vertex::CommonFormat::RGBAub; + cmd.formats[0] = getSinglePositionFormat(is2D); + cmd.formats[1] = CommonFormat::RGBAub; cmd.vertexCount = (int) numpoints; BatchedVertexData data = requestBatchedDraw(cmd); @@ -1612,9 +1611,9 @@ void Graphics::polygon(DrawMode mode, const Vector2 *coords, size_t count, bool bool is2D = t.isAffine2DTransform(); BatchedDrawCommand cmd; - cmd.formats[0] = vertex::getSinglePositionFormat(is2D); - cmd.formats[1] = vertex::CommonFormat::RGBAub; - cmd.indexMode = vertex::TriangleIndexMode::FAN; + cmd.formats[0] = getSinglePositionFormat(is2D); + cmd.formats[1] = CommonFormat::RGBAub; + cmd.indexMode = TRIANGLEINDEX_FAN; cmd.vertexCount = (int)count - (skipLastFilledVertex ? 1 : 0); BatchedVertexData data = requestBatchedDraw(cmd); diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 600ab389d..1a2f75640 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -209,8 +209,8 @@ public: { PrimitiveType primitiveType = PRIMITIVE_TRIANGLES; - const vertex::Attributes *attributes; - const vertex::BufferBindings *buffers; + const VertexAttributes *attributes; + const BufferBindings *buffers; int vertexStart = 0; int vertexCount = 0; @@ -221,7 +221,7 @@ public: // TODO: This should be moved out to a state transition API? CullMode cullMode = CULL_NONE; - DrawCommand(const vertex::Attributes *attribs, const vertex::BufferBindings *buffers) + DrawCommand(const VertexAttributes *attribs, const BufferBindings *buffers) : attributes(attribs) , buffers(buffers) {} @@ -231,8 +231,8 @@ public: { PrimitiveType primitiveType = PRIMITIVE_TRIANGLES; - const vertex::Attributes *attributes; - const vertex::BufferBindings *buffers; + const VertexAttributes *attributes; + const BufferBindings *buffers; int indexCount = 0; int instanceCount = 1; @@ -246,7 +246,7 @@ public: // TODO: This should be moved out to a state transition API? CullMode cullMode = CULL_NONE; - DrawIndexedCommand(const vertex::Attributes *attribs, const vertex::BufferBindings *buffers, Resource *indexbuffer) + DrawIndexedCommand(const VertexAttributes *attribs, const BufferBindings *buffers, Resource *indexbuffer) : attributes(attribs) , buffers(buffers) , indexBuffer(indexbuffer) @@ -256,8 +256,8 @@ public: struct BatchedDrawCommand { PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES; - vertex::CommonFormat formats[2]; - vertex::TriangleIndexMode indexMode = vertex::TriangleIndexMode::NONE; + CommonFormat formats[2]; + TriangleIndexMode indexMode = TRIANGLEINDEX_NONE; int vertexCount = 0; Texture *texture = nullptr; Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT; @@ -265,7 +265,7 @@ public: BatchedDrawCommand() { // VS2013 can't initialize arrays in the above manner... - formats[1] = formats[0] = vertex::CommonFormat::NONE; + formats[1] = formats[0] = CommonFormat::NONE; } }; @@ -428,17 +428,17 @@ public: Font *newDefaultFont(int size, font::TrueTypeRasterizer::Hinting hinting); Video *newVideo(love::video::VideoStream *stream, float dpiscale); - SpriteBatch *newSpriteBatch(Texture *texture, int size, vertex::Usage usage); + SpriteBatch *newSpriteBatch(Texture *texture, int size, BufferUsage usage); ParticleSystem *newParticleSystem(Texture *texture, int size); Shader *newShader(const std::vector &stagessource); - virtual Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) = 0; + virtual Buffer *newBuffer(const Buffer::Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) = 0; + virtual Buffer *newBuffer(const Buffer::Settings &settings, DataFormat format, const void *data, size_t size, size_t arraylength); - Mesh *newMesh(const std::vector &vertices, PrimitiveType drawmode, vertex::Usage usage); - Mesh *newMesh(int vertexcount, PrimitiveType drawmode, vertex::Usage usage); - Mesh *newMesh(const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage); - Mesh *newMesh(const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage); + Mesh *newMesh(const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage); + Mesh *newMesh(const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage); + Mesh *newMesh(const std::vector &attributes, PrimitiveType drawmode); Text *newText(Font *font, const std::vector &text = {}); @@ -582,8 +582,8 @@ public: void setMeshCullMode(CullMode cull); CullMode getMeshCullMode() const; - virtual void setFrontFaceWinding(vertex::Winding winding) = 0; - vertex::Winding getFrontFaceWinding() const; + virtual void setFrontFaceWinding(Winding winding) = 0; + Winding getFrontFaceWinding() const; /** * Sets the enabled color components when rendering. @@ -814,7 +814,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::BufferBindings &buffers, Texture *texture) = 0; + virtual void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, Texture *texture) = 0; void flushBatchedDraws(); BatchedVertexData requestBatchedDraw(const BatchedDrawCommand &command); @@ -885,7 +885,7 @@ protected: bool depthWrite = false; CullMode meshCullMode = CULL_NONE; - vertex::Winding winding = vertex::WINDING_CCW; + Winding winding = WINDING_CCW; StrongRef font; StrongRef shader; @@ -905,7 +905,7 @@ protected: StreamBuffer *indexBuffer = nullptr; PrimitiveType primitiveMode = PRIMITIVE_TRIANGLES; - vertex::CommonFormat formats[2]; + CommonFormat formats[2]; StrongRef texture; Shader::StandardShader standardShaderType = Shader::STANDARD_DEFAULT; int vertexCount = 0; @@ -917,7 +917,7 @@ protected: BatchedDrawState() { vb[0] = vb[1] = nullptr; - formats[0] = formats[1] = vertex::CommonFormat::NONE; + formats[0] = formats[1] = CommonFormat::NONE; vbMap[0] = vbMap[1] = StreamBuffer::MapInfo(); } }; diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index 29183d70a..c85662a08 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -34,36 +34,22 @@ namespace love namespace graphics { -static const char *getBuiltinAttribName(BuiltinVertexAttribute attribid) -{ - const char *name = ""; - vertex::getConstant(attribid, name); - return name; -} - static_assert(offsetof(Vertex, x) == sizeof(float) * 0, "Incorrect position offset in Vertex struct"); static_assert(offsetof(Vertex, s) == sizeof(float) * 2, "Incorrect texture coordinate offset in Vertex struct"); static_assert(offsetof(Vertex, color.r) == sizeof(float) * 4, "Incorrect color offset in Vertex struct"); -std::vector Mesh::getDefaultVertexFormat() +std::vector Mesh::getDefaultVertexFormat() { - // Corresponds to the love::Vertex struct. - std::vector vertexformat = { - { getBuiltinAttribName(ATTRIB_POS), vertex::DATA_FLOAT, 2 }, - { getBuiltinAttribName(ATTRIB_TEXCOORD), vertex::DATA_FLOAT, 2 }, - { getBuiltinAttribName(ATTRIB_COLOR), vertex::DATA_UNORM8, 4 }, - }; - - return vertexformat; + return Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub); } love::Type Mesh::type("Mesh", &Drawable::type); -Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage) - : vertexFormat(vertexformat) - , vertexBuffer(nullptr) +Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage) + : vertexBuffer(nullptr) , vertexCount(0) , vertexStride(0) + , vertexScratchBuffer(nullptr) , indexBuffer(nullptr) , useIndexBuffer(false) , indexCount(0) @@ -72,29 +58,29 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexforma , rangeStart(-1) , rangeCount(-1) { + Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage); + vertexBuffer.set(gfx->newBuffer(settings, vertexformat, data, datasize, 0), Acquire::NORETAIN); + + vertexCount = vertexBuffer->getArrayLength(); + vertexStride = vertexBuffer->getArrayStride(); + vertexFormat = vertexBuffer->getDataMembers(); + setupAttachedAttributes(); - calculateAttributeSizes(gfx); - vertexCount = datasize / vertexStride; - indexDataType = vertex::getIndexDataTypeFromMax(vertexCount); - - if (vertexCount == 0) - throw love::Exception("Data size is too small for specified vertex attribute formats."); - - vertexBuffer = gfx->newBuffer(datasize, data, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ); + indexDataType = getIndexDataTypeFromMax(vertexCount); vertexScratchBuffer = new char[vertexStride]; } -Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage) - : vertexFormat(vertexformat) - , vertexBuffer(nullptr) +Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage) + : vertexBuffer(nullptr) , vertexCount((size_t) vertexcount) , vertexStride(0) + , vertexScratchBuffer(nullptr) , indexBuffer(nullptr) , useIndexBuffer(false) , indexCount(0) - , indexDataType(vertex::getIndexDataTypeFromMax(vertexcount)) + , indexDataType(getIndexDataTypeFromMax(vertexcount)) , primitiveType(drawmode) , rangeStart(-1) , rangeCount(-1) @@ -102,83 +88,82 @@ Mesh::Mesh(graphics::Graphics *gfx, const std::vector &vertexforma if (vertexcount <= 0) throw love::Exception("Invalid number of vertices (%d).", vertexcount); + Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ, usage); + vertexBuffer.set(gfx->newBuffer(settings, vertexformat, nullptr, 0, vertexcount), Acquire::NORETAIN); + + vertexStride = vertexBuffer->getArrayStride(); + vertexFormat = vertexBuffer->getDataMembers(); + setupAttachedAttributes(); - calculateAttributeSizes(gfx); - size_t buffersize = vertexCount * vertexStride; - - vertexBuffer = gfx->newBuffer(buffersize, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY | Buffer::MAP_READ); - - // Initialize the buffer's contents to 0. - memset(vertexBuffer->map(), 0, buffersize); + memset(vertexBuffer->map(), 0, vertexBuffer->getSize()); vertexBuffer->setMappedRangeModified(0, vertexBuffer->getSize()); vertexBuffer->unmap(); vertexScratchBuffer = new char[vertexStride]; } -Mesh::~Mesh() +Mesh::Mesh(const std::vector &attributes, PrimitiveType drawmode) + : vertexBuffer(nullptr) + , vertexCount(0) + , vertexStride(0) + , vertexScratchBuffer(nullptr) + , indexBuffer(nullptr) + , useIndexBuffer(false) + , indexCount(0) + , indexDataType(INDEX_UINT16) + , primitiveType(drawmode) + , rangeStart(-1) + , rangeCount(-1) { - delete vertexBuffer; - delete indexBuffer; - delete vertexScratchBuffer; + if (attributes.size() == 0) + throw love::Exception("At least one buffer attribute must be specified in this constructor."); + + attachedAttributes = attributes; + + vertexCount = attachedAttributes.size() > 0 ? LOVE_UINT32_MAX : 0; for (const auto &attrib : attachedAttributes) { - if (attrib.second.mesh != this) - attrib.second.mesh->release(); + if ((attrib.buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0) + throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute."); + + if (getAttachedAttributeIndex(attrib.name) != -1) + throw love::Exception("Duplicate vertex attribute name: %s", attrib.name.c_str()); + + vertexCount = std::min(vertexCount, attrib.buffer->getArrayLength()); } + + indexDataType = getIndexDataTypeFromMax(vertexCount); +} + +Mesh::~Mesh() +{ + delete vertexScratchBuffer; } void Mesh::setupAttachedAttributes() { for (size_t i = 0; i < vertexFormat.size(); i++) { - const std::string &name = vertexFormat[i].name; + const std::string &name = vertexFormat[i].decl.name; - if (attachedAttributes.find(name) != attachedAttributes.end()) + if (getAttachedAttributeIndex(name) != -1) throw love::Exception("Duplicate vertex attribute name: %s", name.c_str()); - attachedAttributes[name] = {this, (int) i, STEP_PER_VERTEX, true}; + attachedAttributes.push_back({name, vertexBuffer, (int) i, STEP_PER_VERTEX, true}); } } -void Mesh::calculateAttributeSizes(Graphics *gfx) +int Mesh::getAttachedAttributeIndex(const std::string &name) const { - bool supportsGLSL3 = gfx->getCapabilities().features[Graphics::FEATURE_GLSL3]; - - size_t stride = 0; - - for (const AttribFormat &format : vertexFormat) + for (int i = 0; i < (int) attachedAttributes.size(); i++) { - size_t size = vertex::getDataTypeSize(format.type) * format.components; - - if (format.components <= 0 || format.components > 4) - throw love::Exception("Vertex attributes must have between 1 and 4 components."); - - // Hardware really doesn't like attributes that aren't 32 bit-aligned. - if (size % 4 != 0) - throw love::Exception("Vertex attributes must have enough components to be a multiple of 32 bits."); - - if (vertex::isDataTypeInteger(format.type) && !supportsGLSL3) - throw love::Exception("Integer vertex attribute data types require GLSL 3 support."); - - // Total size in bytes of each attribute in a single vertex. - attributeSizes.push_back(size); - stride += size; + if (attachedAttributes[i].name == name) + return i; } - vertexStride = stride; -} - -size_t Mesh::getAttributeOffset(size_t attribindex) const -{ - size_t offset = 0; - - for (size_t i = 0; i < attribindex; i++) - offset += attributeSizes[i]; - - return offset; + return -1; } void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize) @@ -186,6 +171,9 @@ void Mesh::setVertex(size_t vertindex, const void *data, size_t datasize) if (vertindex >= vertexCount) throw love::Exception("Invalid vertex index: %ld", vertindex + 1); + if (vertexBuffer.get() == nullptr) + throw love::Exception("setVertex can only be called on a Mesh which owns its own vertex buffer."); + size_t offset = vertindex * vertexStride; size_t size = std::min(datasize, vertexStride); @@ -200,6 +188,9 @@ size_t Mesh::getVertex(size_t vertindex, void *data, size_t datasize) if (vertindex >= vertexCount) throw love::Exception("Invalid vertex index: %ld", vertindex + 1); + if (vertexBuffer.get() == nullptr) + throw love::Exception("getVertex can only be called on a Mesh which owns its own vertex buffer."); + size_t offset = vertindex * vertexStride; size_t size = std::min(datasize, vertexStride); @@ -223,8 +214,13 @@ void Mesh::setVertexAttribute(size_t vertindex, int attribindex, const void *dat if (attribindex >= (int) vertexFormat.size()) throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1); - size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex); - size_t size = std::min(datasize, attributeSizes[attribindex]); + if (vertexBuffer.get() == nullptr) + throw love::Exception("setVertexAttribute can only be called on a Mesh which owns its own vertex buffer."); + + const auto &member = vertexFormat[attribindex]; + + size_t offset = vertindex * vertexStride + member.offset; + size_t size = std::min(datasize, member.info.size); uint8 *bufferdata = (uint8 *) vertexBuffer->map(); memcpy(bufferdata + offset, data, size); @@ -240,8 +236,13 @@ size_t Mesh::getVertexAttribute(size_t vertindex, int attribindex, void *data, s if (attribindex >= (int) vertexFormat.size()) throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1); - size_t offset = vertindex * vertexStride + getAttributeOffset(attribindex); - size_t size = std::min(datasize, attributeSizes[attribindex]); + if (vertexBuffer.get() == nullptr) + throw love::Exception("getVertexAttribute can only be called on a Mesh which owns its own vertex buffer."); + + const auto &member = vertexFormat[attribindex]; + + size_t offset = vertindex * vertexStride + member.offset; + size_t size = std::min(datasize, member.info.size); // We're relying on map() returning read/write data... ew. const uint8 *bufferdata = (const uint8 *) vertexBuffer->map(); @@ -260,128 +261,106 @@ size_t Mesh::getVertexStride() const return vertexStride; } -const std::vector &Mesh::getVertexFormat() const +Buffer *Mesh::getVertexBuffer() const +{ + return vertexBuffer; +} + +const std::vector &Mesh::getVertexFormat() const { return vertexFormat; } -vertex::DataType Mesh::getAttributeInfo(int attribindex, int &components) const -{ - if (attribindex < 0 || attribindex >= (int) vertexFormat.size()) - throw love::Exception("Invalid vertex attribute index: %d", attribindex + 1); - - components = vertexFormat[attribindex].components; - return vertexFormat[attribindex].type; -} - -int Mesh::getAttributeIndex(const std::string &name) const -{ - for (int i = 0; i < (int) vertexFormat.size(); i++) - { - if (vertexFormat[i].name == name) - return i; - } - - return -1; -} - void Mesh::setAttributeEnabled(const std::string &name, bool enable) { - auto it = attachedAttributes.find(name); - - if (it == attachedAttributes.end()) + int index = getAttachedAttributeIndex(name); + if (index == -1) throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str()); - it->second.enabled = enable; + attachedAttributes[index].enabled = enable; } bool Mesh::isAttributeEnabled(const std::string &name) const { - const auto it = attachedAttributes.find(name); - - if (it == attachedAttributes.end()) + int index = getAttachedAttributeIndex(name); + if (index == -1) throw love::Exception("Mesh does not have an attached vertex attribute named '%s'", name.c_str()); - return it->second.enabled; + return attachedAttributes[index].enabled; } -void Mesh::attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step) +void Mesh::attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step) { + if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0) + throw love::Exception("Buffer must be created with vertex buffer support to be used as a Mesh vertex attribute."); + auto gfx = Module::getInstance(Module::M_GRAPHICS); if (step == STEP_PER_INSTANCE && !gfx->getCapabilities().features[Graphics::FEATURE_INSTANCING]) throw love::Exception("Vertex attribute instancing is not supported on this system."); - if (mesh != this) - { - for (const auto &it : mesh->attachedAttributes) - { - // If the supplied Mesh has attached attributes of its own, then we - // prevent it from being attached to avoid reference cycles. - if (it.second.mesh != mesh) - throw love::Exception("Cannot attach a Mesh which has attached Meshes of its own."); - } - } + BufferAttribute oldattrib = {}; + BufferAttribute newattrib = {}; - AttachedAttribute oldattrib = {}; - AttachedAttribute newattrib = {}; + int oldindex = getAttachedAttributeIndex(name); + if (oldindex != -1) + oldattrib = attachedAttributes[oldindex]; + else if (attachedAttributes.size() + 1 > VertexAttributes::MAX) + throw love::Exception("A maximum of %d attributes can be attached at once.", VertexAttributes::MAX); - 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; - newattrib.index = mesh->getAttributeIndex(attachname); + newattrib.name = name; + newattrib.buffer = buffer; + newattrib.enabled = oldattrib.buffer.get() ? oldattrib.enabled : true; + newattrib.indexInBuffer = buffer->getDataMemberIndex(attachname); newattrib.step = step; - if (newattrib.index < 0) - throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", attachname.c_str()); + if (newattrib.indexInBuffer < 0) + throw love::Exception("The specified vertex buffer does not have a vertex attribute named '%s'", attachname.c_str()); - if (newattrib.mesh != this) - newattrib.mesh->retain(); - - attachedAttributes[name] = newattrib; - - if (oldattrib.mesh && oldattrib.mesh != this) - oldattrib.mesh->release(); + if (oldindex != -1) + attachedAttributes[oldindex] = newattrib; + else + attachedAttributes.push_back(newattrib); } bool Mesh::detachAttribute(const std::string &name) { - auto it = attachedAttributes.find(name); + int index = getAttachedAttributeIndex(name); + if (index == -1) + return false; - if (it != attachedAttributes.end() && it->second.mesh != this) - { - it->second.mesh->release(); - attachedAttributes.erase(it); + attachedAttributes.erase(attachedAttributes.begin() + index); - if (getAttributeIndex(name) != -1) - attachAttribute(name, this, name); + if (vertexBuffer.get() && vertexBuffer->getDataMemberIndex(name) != -1) + attachAttribute(name, vertexBuffer, name); - return true; - } + return true; +} - return false; +const std::vector &Mesh::getAttachedAttributes() const +{ + return attachedAttributes; } void *Mesh::mapVertexData() { - return vertexBuffer->map(); + return vertexBuffer.get() != nullptr ? vertexBuffer->map() : nullptr; } void Mesh::unmapVertexData(size_t modifiedoffset, size_t modifiedsize) { + if (!vertexBuffer.get()) + return; + vertexBuffer->setMappedRangeModified(modifiedoffset, modifiedsize); vertexBuffer->unmap(); } void Mesh::flush() { - vertexBuffer->unmap(); + if (vertexBuffer.get()) + vertexBuffer->unmap(); - if (indexBuffer != nullptr) + if (indexBuffer.get()) indexBuffer->unmap(); } @@ -391,7 +370,7 @@ void Mesh::flush() template static void copyToIndexBuffer(const std::vector &indices, Buffer::Mapper &buffermap, size_t maxval) { - T *elems = (T *) buffermap.get(); + T *elems = (T *) buffermap.data; for (size_t i = 0; i < indices.size(); i++) { @@ -406,21 +385,18 @@ void Mesh::setVertexMap(const std::vector &map) { size_t maxval = getVertexCount(); - IndexDataType datatype = vertex::getIndexDataTypeFromMax(maxval); + IndexDataType datatype = getIndexDataTypeFromMax(maxval); + DataFormat dataformat = getIndexDataFormat(datatype); // Calculate the size in bytes of the index buffer data. - size_t size = map.size() * vertex::getIndexDataSize(datatype); + size_t size = map.size() * getIndexDataSize(datatype); - if (indexBuffer && size > indexBuffer->getSize()) - { - delete indexBuffer; - indexBuffer = nullptr; - } - - if (!indexBuffer && size > 0) + if (indexBuffer.get() == nullptr || size > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat) { auto gfx = Module::getInstance(Module::M_GRAPHICS); - indexBuffer = gfx->newBuffer(size, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ); + auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC; + Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage); + indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, size, 0), Acquire::NORETAIN); } useIndexBuffer = true; @@ -448,25 +424,23 @@ void Mesh::setVertexMap(const std::vector &map) void Mesh::setVertexMap(IndexDataType datatype, const void *data, size_t datasize) { - if (indexBuffer && datasize > indexBuffer->getSize()) - { - delete indexBuffer; - indexBuffer = nullptr; - } + DataFormat dataformat = getIndexDataFormat(datatype); - if (!indexBuffer && datasize > 0) + if (indexBuffer.get() == nullptr || datasize > indexBuffer->getSize() || indexBuffer->getDataMember(0).decl.format != dataformat) { auto gfx = Module::getInstance(Module::M_GRAPHICS); - indexBuffer = gfx->newBuffer(datasize, nullptr, BUFFER_INDEX, vertexBuffer->getUsage(), Buffer::MAP_READ); + auto usage = vertexBuffer.get() ? vertexBuffer->getUsage() : BUFFERUSAGE_DYNAMIC; + Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_READ, usage); + indexBuffer.set(gfx->newBuffer(settings, dataformat, nullptr, datasize, 0), Acquire::NORETAIN); } - indexCount = datasize / vertex::getIndexDataSize(datatype); + indexCount = datasize / getIndexDataSize(datatype); if (!indexBuffer || indexCount == 0) return; Buffer::Mapper ibomap(*indexBuffer); - memcpy(ibomap.get(), data, datasize); + memcpy(ibomap.data, data, datasize); useIndexBuffer = true; indexDataType = datatype; @@ -499,6 +473,9 @@ bool Mesh::getVertexMap(std::vector &map) const if (!indexBuffer || indexCount == 0) return true; + if ((indexBuffer->getMapFlags() & Buffer::MAP_READ) == 0) + return false; + // We unmap the buffer in Mesh::draw, Mesh::setVertexMap, and Mesh::flush. void *buffer = indexBuffer->map(); @@ -517,7 +494,27 @@ bool Mesh::getVertexMap(std::vector &map) const return true; } -size_t Mesh::getVertexMapCount() const +void Mesh::setIndexBuffer(Buffer *buffer) +{ + // Buffer constructor does the rest of the validation for index buffers + // (data member formats, etc.) + if (buffer != nullptr && (buffer->getTypeFlags() & Buffer::TYPEFLAG_INDEX) == 0) + throw love::Exception("setIndexBuffer requires a Buffer created as an index buffer."); + + indexBuffer.set(buffer); + useIndexBuffer = buffer != nullptr; + indexCount = buffer != nullptr ? buffer->getArrayLength() : 0; + + if (buffer != nullptr) + indexDataType = getIndexDataType(buffer->getDataMember(0).decl.format); +} + +Buffer *Mesh::getIndexBuffer() const +{ + return indexBuffer; +} + +size_t Mesh::getIndexCount() const { return indexCount; } @@ -592,43 +589,42 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) if (Shader::current && texture.get()) Shader::current->checkMainTexture(texture); - vertex::Attributes attributes; - vertex::BufferBindings buffers; + VertexAttributes attributes; + BufferBindings buffers; int activebuffers = 0; for (const auto &attrib : attachedAttributes) { - if (!attrib.second.enabled) + if (!attrib.enabled) continue; - Mesh *mesh = attrib.second.mesh; + Buffer *buffer = attrib.buffer.get(); 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. BuiltinVertexAttribute builtinattrib; - if (vertex::getConstant(attrib.first.c_str(), builtinattrib)) + if (getConstant(attrib.name.c_str(), builtinattrib)) attributeindex = (int) builtinattrib; else if (Shader::current) - attributeindex = Shader::current->getVertexAttributeIndex(attrib.first); + attributeindex = Shader::current->getVertexAttributeIndex(attrib.name); if (attributeindex >= 0) { // Make sure the buffer isn't mapped (sends data to GPU if needed.) - mesh->vertexBuffer->unmap(); + buffer->unmap(); - const auto &formats = mesh->getVertexFormat(); - const auto &format = formats[attrib.second.index]; + const auto &member = buffer->getDataMember(attrib.indexInBuffer); - uint16 offset = (uint16) mesh->getAttributeOffset(attrib.second.index); - uint16 stride = (uint16) mesh->getVertexStride(); + uint16 offset = (uint16) member.offset; + uint16 stride = (uint16) buffer->getArrayStride(); - attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers); - attributes.setBufferLayout(activebuffers, stride, attrib.second.step); + attributes.set(attributeindex, member.decl.format, offset, activebuffers); + attributes.setBufferLayout(activebuffers, stride, attrib.step); // TODO: Ideally we want to reuse buffers with the same stride+step. - buffers.set(activebuffers, mesh->vertexBuffer, 0); + buffers.set(activebuffers, buffer, 0); activebuffers++; } } @@ -653,7 +649,7 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) cmd.cullMode = gfx->getMeshCullMode(); int start = std::min(std::max(0, rangeStart), (int) indexCount - 1); - cmd.indexBufferOffset = start * vertex::getIndexDataSize(indexDataType); + cmd.indexBufferOffset = start * getIndexDataSize(indexDataType); cmd.indexCount = (int) indexCount; if (rangeCount > 0) diff --git a/src/modules/graphics/Mesh.h b/src/modules/graphics/Mesh.h index 148fc150d..72994f051 100644 --- a/src/modules/graphics/Mesh.h +++ b/src/modules/graphics/Mesh.h @@ -50,17 +50,20 @@ class Mesh : public Drawable { public: - static love::Type type; - - struct AttribFormat + struct BufferAttribute { std::string name; - vertex::DataType type; - int components; // max 4 + StrongRef buffer; + int indexInBuffer; + AttributeStep step; + bool enabled; }; - Mesh(Graphics *gfx, const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, vertex::Usage usage); - Mesh(Graphics *gfx, const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, vertex::Usage usage); + static love::Type type; + + Mesh(Graphics *gfx, const std::vector &vertexformat, const void *data, size_t datasize, PrimitiveType drawmode, BufferUsage usage); + Mesh(Graphics *gfx, const std::vector &vertexformat, int vertexcount, PrimitiveType drawmode, BufferUsage usage); + Mesh(const std::vector &attributes, PrimitiveType drawmode); virtual ~Mesh(); @@ -92,12 +95,15 @@ public: **/ size_t getVertexStride() const; + /** + * Gets the Buffer that holds the Mesh's vertices. + **/ + Buffer *getVertexBuffer() const; + /** * Gets the format of each vertex attribute stored in the Mesh. **/ - const std::vector &getVertexFormat() const; - vertex::DataType getAttributeInfo(int attribindex, int &components) const; - int getAttributeIndex(const std::string &name) const; + const std::vector &getVertexFormat() const; /** * Sets whether a specific vertex attribute is used when drawing the Mesh. @@ -106,11 +112,12 @@ public: bool isAttributeEnabled(const std::string &name) const; /** - * Attaches a vertex attribute from another Mesh to this one. The attribute - * will be used when drawing this Mesh. + * Attaches a vertex attribute from another vertex buffer to this Mesh. The + * attribute will be used when drawing this Mesh. **/ - void attachAttribute(const std::string &name, Mesh *mesh, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX); + void attachAttribute(const std::string &name, Buffer *buffer, const std::string &attachname, AttributeStep step = STEP_PER_VERTEX); bool detachAttribute(const std::string &name); + const std::vector &getAttachedAttributes() const; void *mapVertexData(); void unmapVertexData(size_t modifiedoffset = 0, size_t modifiedsize = -1); @@ -136,10 +143,13 @@ public: **/ bool getVertexMap(std::vector &map) const; + void setIndexBuffer(Buffer *buffer); + Buffer *getIndexBuffer() const; + /** * Gets the total number of elements in the vertex map array. **/ - size_t getVertexMapCount() const; + size_t getIndexCount() const; /** * Sets the texture used when drawing the Mesh. @@ -172,31 +182,21 @@ public: void drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount); - static std::vector getDefaultVertexFormat(); + static std::vector getDefaultVertexFormat(); private: friend class SpriteBatch; - struct AttachedAttribute - { - Mesh *mesh; - int index; - AttributeStep step; - bool enabled; - }; - void setupAttachedAttributes(); - void calculateAttributeSizes(Graphics *gfx); - size_t getAttributeOffset(size_t attribindex) const; + int getAttachedAttributeIndex(const std::string &name) const; - std::vector vertexFormat; - std::vector attributeSizes; + std::vector vertexFormat; - std::unordered_map attachedAttributes; + std::vector attachedAttributes; // Vertex buffer, for the vertex data. - Buffer *vertexBuffer; + StrongRef vertexBuffer; size_t vertexCount; size_t vertexStride; @@ -205,7 +205,7 @@ private: char *vertexScratchBuffer; // Index buffer, for the vertex map. - Buffer *indexBuffer; + StrongRef indexBuffer; bool useIndexBuffer; size_t indexCount; IndexDataType indexDataType; diff --git a/src/modules/graphics/ParticleSystem.cpp b/src/modules/graphics/ParticleSystem.cpp index fac406e96..f074c5d7a 100644 --- a/src/modules/graphics/ParticleSystem.cpp +++ b/src/modules/graphics/ParticleSystem.cpp @@ -93,7 +93,7 @@ ParticleSystem::ParticleSystem(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) + , vertexAttributes(CommonFormat::XYf_STf_RGBAub, 0) , buffer(nullptr) { if (size == 0 || size > MAX_PARTICLES) @@ -191,7 +191,9 @@ void ParticleSystem::createBuffers(size_t size) auto gfx = Module::getInstance(Module::M_GRAPHICS); size_t bytes = sizeof(Vertex) * size * 4; - buffer = gfx->newBuffer(bytes, nullptr, BUFFER_VERTEX, vertex::USAGE_STREAM, 0); + Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_STREAM); + auto decl = Buffer::getCommonFormatDeclaration(CommonFormat::XYf_STf_RGBAub); + buffer = gfx->newBuffer(settings, decl, nullptr, bytes, 0); } catch (std::bad_alloc &) { @@ -203,7 +205,8 @@ void ParticleSystem::createBuffers(size_t size) void ParticleSystem::deleteBuffers() { delete[] pMem; - delete buffer; + if (buffer) + buffer->release(); pMem = nullptr; buffer = nullptr; @@ -1080,7 +1083,7 @@ void ParticleSystem::draw(Graphics *gfx, const Matrix4 &m) Graphics::TempTransform transform(gfx, m); - vertex::BufferBindings vertexbuffers; + BufferBindings vertexbuffers; vertexbuffers.set(0, buffer, 0); gfx->drawQuads(0, pCount, vertexAttributes, vertexbuffers, texture); diff --git a/src/modules/graphics/ParticleSystem.h b/src/modules/graphics/ParticleSystem.h index b220e6275..374cd846c 100644 --- a/src/modules/graphics/ParticleSystem.h +++ b/src/modules/graphics/ParticleSystem.h @@ -673,7 +673,7 @@ private: bool relativeRotation; - const vertex::Attributes vertexAttributes; + const VertexAttributes vertexAttributes; Buffer *buffer; static StringMap::Entry distributionsEntries[]; diff --git a/src/modules/graphics/Polyline.cpp b/src/modules/graphics/Polyline.cpp index b5a910d1f..a7972cc59 100644 --- a/src/modules/graphics/Polyline.cpp +++ b/src/modules/graphics/Polyline.cpp @@ -82,7 +82,7 @@ void Polyline::render(const Vector2 *coords, size_t count, size_t size_hint, flo // extra degenerate triangle in between the core line and the overdraw // line in order to break up the strip into two. This will let us draw // everything in one draw call. - if (triangle_mode == vertex::TriangleIndexMode::STRIP) + if (triangle_mode == TRIANGLEINDEX_STRIP) extra_vertices = 2; } @@ -383,7 +383,7 @@ void Polyline::draw(love::graphics::Graphics *gfx) int maxvertices = LOVE_UINT16_MAX - 3; int advance = maxvertices; - if (triangle_mode == vertex::TriangleIndexMode::STRIP) + if (triangle_mode == TRIANGLEINDEX_STRIP) advance -= 2; for (int vertex_start = 0; vertex_start < total_vertex_count; vertex_start += advance) @@ -391,8 +391,8 @@ void Polyline::draw(love::graphics::Graphics *gfx) const Vector2 *verts = vertices + vertex_start; Graphics::BatchedDrawCommand cmd; - cmd.formats[0] = vertex::getSinglePositionFormat(is2D); - cmd.formats[1] = vertex::CommonFormat::RGBAub; + cmd.formats[0] = getSinglePositionFormat(is2D); + cmd.formats[1] = CommonFormat::RGBAub; cmd.indexMode = triangle_mode; cmd.vertexCount = std::min(maxvertices, total_vertex_count - vertex_start); diff --git a/src/modules/graphics/Polyline.h b/src/modules/graphics/Polyline.h index 659256b4e..32bd4c621 100644 --- a/src/modules/graphics/Polyline.h +++ b/src/modules/graphics/Polyline.h @@ -44,7 +44,7 @@ class Polyline { public: - Polyline(vertex::TriangleIndexMode mode = vertex::TriangleIndexMode::STRIP) + Polyline(TriangleIndexMode mode = TRIANGLEINDEX_STRIP) : vertices(nullptr) , overdraw(nullptr) , vertex_count(0) @@ -94,7 +94,7 @@ protected: Vector2 *overdraw; size_t vertex_count; size_t overdraw_vertex_count; - vertex::TriangleIndexMode triangle_mode; + TriangleIndexMode triangle_mode; size_t overdraw_vertex_start; }; // Polyline @@ -109,7 +109,7 @@ class NoneJoinPolyline : public Polyline public: NoneJoinPolyline() - : Polyline(vertex::TriangleIndexMode::QUADS) + : Polyline(TRIANGLEINDEX_QUADS) {} void render(const Vector2 *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw) diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index 07628c797..f6b9b2a08 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -33,11 +33,6 @@ #include #include -namespace glslang -{ -class TShader; -} - namespace love { namespace graphics diff --git a/src/modules/graphics/SpriteBatch.cpp b/src/modules/graphics/SpriteBatch.cpp index d62729d32..677f69352 100644 --- a/src/modules/graphics/SpriteBatch.cpp +++ b/src/modules/graphics/SpriteBatch.cpp @@ -40,7 +40,7 @@ namespace graphics love::Type SpriteBatch::type("SpriteBatch", &Drawable::type); -SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage) +SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage usage) : texture(texture) , size(size) , next(0) @@ -57,14 +57,16 @@ SpriteBatch::SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usag throw love::Exception("A texture must be used when creating a SpriteBatch."); if (texture->getTextureType() == TEXTURE_2D_ARRAY) - vertex_format = vertex::CommonFormat::XYf_STPf_RGBAub; + vertex_format = CommonFormat::XYf_STPf_RGBAub; else - vertex_format = vertex::CommonFormat::XYf_STf_RGBAub; + vertex_format = CommonFormat::XYf_STf_RGBAub; - vertex_stride = vertex::getFormatStride(vertex_format); + vertex_stride = getFormatStride(vertex_format); size_t vertex_size = vertex_stride * 4 * size; - array_buf = gfx->newBuffer(vertex_size, nullptr, BUFFER_VERTEX, usage, Buffer::MAP_EXPLICIT_RANGE_MODIFY); + Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, usage); + auto decl = Buffer::getCommonFormatDeclaration(vertex_format); + array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0); } SpriteBatch::~SpriteBatch() @@ -79,8 +81,6 @@ int SpriteBatch::add(const Matrix4 &m, int index /*= -1*/) int SpriteBatch::add(Quad *quad, const Matrix4 &m, int index /*= -1*/) { - using namespace vertex; - if (vertex_format == CommonFormat::XYf_STPf_RGBAub) return addLayer(quad->getLayer(), quad, m, index); @@ -122,8 +122,6 @@ int SpriteBatch::addLayer(int layer, const Matrix4 &m, int index) int SpriteBatch::addLayer(int layer, Quad *quad, const Matrix4 &m, int index) { - using namespace vertex; - if (vertex_format != CommonFormat::XYf_STPf_RGBAub) throw love::Exception("addLayer can only be called on a SpriteBatch that uses an Array Texture!"); @@ -222,7 +220,9 @@ void SpriteBatch::setBufferSize(int newsize) try { auto gfx = Module::getInstance(Module::M_GRAPHICS); - new_array_buf = gfx->newBuffer(vertex_size, nullptr, array_buf->getType(), array_buf->getUsage(), array_buf->getMapFlags()); + Buffer::Settings settings(array_buf->getTypeFlags(), array_buf->getMapFlags(), array_buf->getUsage()); + auto decl = Buffer::getCommonFormatDeclaration(vertex_format); + new_array_buf = gfx->newBuffer(settings, decl, nullptr, vertex_size, 0); // Copy as much of the old data into the new GLBuffer as can fit. size_t copy_size = vertex_stride * 4 * new_next; @@ -248,24 +248,27 @@ int SpriteBatch::getBufferSize() const return size; } -void SpriteBatch::attachAttribute(const std::string &name, Mesh *mesh) +void SpriteBatch::attachAttribute(const std::string &name, Buffer *buffer) { + if ((buffer->getTypeFlags() & Buffer::TYPEFLAG_VERTEX) == 0) + throw love::Exception("GraphicsBuffer must be created with vertex buffer support to be used as a SpriteBatch vertex attribute."); + AttachedAttribute oldattrib = {}; AttachedAttribute newattrib = {}; - if (mesh->getVertexCount() < (size_t) next * 4) - throw love::Exception("Mesh has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4); + if (buffer->getArrayLength() < (size_t) next * 4) + throw love::Exception("Buffer has too few vertices to be attached to this SpriteBatch (at least %d vertices are required)", next*4); auto it = attached_attributes.find(name); if (it != attached_attributes.end()) oldattrib = it->second; - newattrib.index = mesh->getAttributeIndex(name); + newattrib.index = buffer->getDataMemberIndex(name); if (newattrib.index < 0) - throw love::Exception("The specified mesh does not have a vertex attribute named '%s'", name.c_str()); + throw love::Exception("The specified Buffer does not have a vertex attribute named '%s'", name.c_str()); - newattrib.mesh = mesh; + newattrib.buffer = buffer; attached_attributes[name] = newattrib; } @@ -296,8 +299,6 @@ bool SpriteBatch::getDrawRange(int &start, int &count) const void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) { - using namespace vertex; - if (next == 0) return; @@ -321,7 +322,7 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) // Make sure the buffer isn't mapped when we draw (sends data to GPU if needed.) array_buf->unmap(); - Attributes attributes; + VertexAttributes attributes; BufferBindings buffers; { @@ -333,19 +334,19 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) for (const auto &it : attached_attributes) { - Mesh *mesh = it.second.mesh.get(); + Buffer *buffer = it.second.buffer.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()); + if (buffer->getArrayLength() < (size_t) next * 4) + throw love::Exception("Buffer 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. BuiltinVertexAttribute builtinattrib; - if (vertex::getConstant(it.first.c_str(), builtinattrib)) + if (getConstant(it.first.c_str(), builtinattrib)) attributeindex = (int) builtinattrib; else if (Shader::current) attributeindex = Shader::current->getVertexAttributeIndex(it.first); @@ -353,19 +354,18 @@ void SpriteBatch::draw(Graphics *gfx, const Matrix4 &m) if (attributeindex >= 0) { // Make sure the buffer isn't mapped (sends data to GPU if needed.) - mesh->vertexBuffer->unmap(); + buffer->unmap(); - const auto &formats = mesh->getVertexFormat(); - const auto &format = formats[it.second.index]; + const auto &member = buffer->getDataMember(it.second.index); - uint16 offset = (uint16) mesh->getAttributeOffset(it.second.index); - uint16 stride = (uint16) mesh->getVertexStride(); + uint16 offset = (uint16) buffer->getMemberOffset(it.second.index); + uint16 stride = (uint16) buffer->getArrayStride(); - attributes.set(attributeindex, format.type, (uint8) format.components, offset, activebuffers); + attributes.set(attributeindex, member.decl.format, offset, activebuffers); attributes.setBufferLayout(activebuffers, stride); // TODO: We should reuse buffer bindings with the same buffer+stride+step. - buffers.set(activebuffers, mesh->vertexBuffer, 0); + buffers.set(activebuffers, buffer, 0); activebuffers++; } } diff --git a/src/modules/graphics/SpriteBatch.h b/src/modules/graphics/SpriteBatch.h index 644a82f03..40fbfd932 100644 --- a/src/modules/graphics/SpriteBatch.h +++ b/src/modules/graphics/SpriteBatch.h @@ -51,7 +51,7 @@ public: static love::Type type; - SpriteBatch(Graphics *gfx, Texture *texture, int size, vertex::Usage usage); + SpriteBatch(Graphics *gfx, Texture *texture, int size, BufferUsage usage); virtual ~SpriteBatch(); int add(const Matrix4 &m, int index = -1); @@ -93,7 +93,7 @@ public: * Attaches a specific vertex attribute from a Mesh to this SpriteBatch. * The vertex attribute will be used when drawing the SpriteBatch. **/ - void attachAttribute(const std::string &name, Mesh *mesh); + void attachAttribute(const std::string &name, Buffer *buffer); void setDrawRange(int start, int count); void setDrawRange(); @@ -106,7 +106,7 @@ private: struct AttachedAttribute { - StrongRef mesh; + StrongRef buffer; int index; }; @@ -128,7 +128,7 @@ private: Color32 color; Colorf colorf; - vertex::CommonFormat vertex_format; + CommonFormat vertex_format; size_t vertex_stride; love::graphics::Buffer *array_buf; diff --git a/src/modules/graphics/Text.cpp b/src/modules/graphics/Text.cpp index 1ea624200..3b3d54d57 100644 --- a/src/modules/graphics/Text.cpp +++ b/src/modules/graphics/Text.cpp @@ -42,7 +42,8 @@ Text::Text(Font *font, const std::vector &text) Text::~Text() { - delete vertex_buffer; + if (vertex_buffer) + vertex_buffer->release(); } void Text::uploadVertices(const std::vector &vertices, size_t vertoffset) @@ -60,12 +61,14 @@ void Text::uploadVertices(const std::vector &vertices, size_t newsize = std::max(size_t(vertex_buffer->getSize() * 1.5), newsize); auto gfx = Module::getInstance(Module::M_GRAPHICS); - Buffer *new_buffer = gfx->newBuffer(newsize, nullptr, BUFFER_VERTEX, vertex::USAGE_DYNAMIC, 0); + Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, 0, BUFFERUSAGE_DYNAMIC); + auto decl = Buffer::getCommonFormatDeclaration(Font::vertexFormat); + Buffer *new_buffer = gfx->newBuffer(settings, decl, nullptr, newsize, 0); if (vertex_buffer != nullptr) vertex_buffer->copyTo(0, vertex_buffer->getSize(), new_buffer, 0); - delete vertex_buffer; + vertex_buffer->release(); vertex_buffer = new_buffer; vertexBuffers.set(0, vertex_buffer, 0); diff --git a/src/modules/graphics/Text.h b/src/modules/graphics/Text.h index ab858f86b..0e3259919 100644 --- a/src/modules/graphics/Text.h +++ b/src/modules/graphics/Text.h @@ -85,8 +85,8 @@ private: StrongRef font; - vertex::Attributes vertexAttributes; - vertex::BufferBindings vertexBuffers; + VertexAttributes vertexAttributes; + BufferBindings vertexBuffers; Buffer *vertex_buffer; diff --git a/src/modules/graphics/Texture.cpp b/src/modules/graphics/Texture.cpp index 071157493..99b28e0ed 100644 --- a/src/modules/graphics/Texture.cpp +++ b/src/modules/graphics/Texture.cpp @@ -309,8 +309,6 @@ void Texture::draw(Graphics *gfx, const Matrix4 &m) void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform) { - using namespace vertex; - if (!readable) throw love::Exception("Textures with non-readable formats cannot be drawn."); @@ -327,9 +325,9 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform) bool is2D = tm.isAffine2DTransform(); Graphics::BatchedDrawCommand cmd; - cmd.formats[0] = vertex::getSinglePositionFormat(is2D); + cmd.formats[0] = getSinglePositionFormat(is2D); cmd.formats[1] = CommonFormat::STf_RGBAub; - cmd.indexMode = TriangleIndexMode::QUADS; + cmd.indexMode = TRIANGLEINDEX_QUADS; cmd.vertexCount = 4; cmd.texture = this; @@ -343,7 +341,7 @@ void Texture::draw(Graphics *gfx, Quad *q, const Matrix4 &localTransform) t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4); const Vector2 *texcoords = q->getVertexTexCoords(); - vertex::STf_RGBAub *vertexdata = (vertex::STf_RGBAub *) data.stream[1]; + STf_RGBAub *vertexdata = (STf_RGBAub *) data.stream[1]; Color32 c = toColor32(gfx->getColor()); @@ -362,8 +360,6 @@ void Texture::drawLayer(Graphics *gfx, int layer, const Matrix4 &m) void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m) { - using namespace vertex; - if (!readable) throw love::Exception("Textures with non-readable formats cannot be drawn."); @@ -384,9 +380,9 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m) Matrix4 t(tm, m); Graphics::BatchedDrawCommand cmd; - cmd.formats[0] = vertex::getSinglePositionFormat(is2D); + cmd.formats[0] = getSinglePositionFormat(is2D); cmd.formats[1] = CommonFormat::STPf_RGBAub; - cmd.indexMode = TriangleIndexMode::QUADS; + cmd.indexMode = TRIANGLEINDEX_QUADS; cmd.vertexCount = 4; cmd.texture = this; cmd.standardShaderType = Shader::STANDARD_ARRAY; @@ -399,7 +395,7 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m) t.transformXY0((Vector3 *) data.stream[0], q->getVertexPositions(), 4); const Vector2 *texcoords = q->getVertexTexCoords(); - vertex::STPf_RGBAub *vertexdata = (vertex::STPf_RGBAub *) data.stream[1]; + STPf_RGBAub *vertexdata = (STPf_RGBAub *) data.stream[1]; for (int i = 0; i < 4; i++) { diff --git a/src/modules/graphics/Video.cpp b/src/modules/graphics/Video.cpp index 170ff08b2..0a05efb78 100644 --- a/src/modules/graphics/Video.cpp +++ b/src/modules/graphics/Video.cpp @@ -121,9 +121,9 @@ void Video::draw(Graphics *gfx, const Matrix4 &m) Matrix4 t(tm, m); Graphics::BatchedDrawCommand cmd; - cmd.formats[0] = vertex::getSinglePositionFormat(is2D); - cmd.formats[1] = vertex::CommonFormat::STf_RGBAub; - cmd.indexMode = vertex::TriangleIndexMode::QUADS; + cmd.formats[0] = getSinglePositionFormat(is2D); + cmd.formats[1] = CommonFormat::STf_RGBAub; + cmd.indexMode = TRIANGLEINDEX_QUADS; cmd.vertexCount = 4; cmd.standardShaderType = Shader::STANDARD_VIDEO; @@ -134,7 +134,7 @@ void Video::draw(Graphics *gfx, const Matrix4 &m) else t.transformXY0((Vector3 *) data.stream[0], vertices, 4); - vertex::STf_RGBAub *verts = (vertex::STf_RGBAub *) data.stream[1]; + STf_RGBAub *verts = (STf_RGBAub *) data.stream[1]; Color32 c = toColor32(gfx->getColor()); diff --git a/src/modules/graphics/opengl/Buffer.cpp b/src/modules/graphics/opengl/Buffer.cpp index b50227552..fbd8eae95 100644 --- a/src/modules/graphics/opengl/Buffer.cpp +++ b/src/modules/graphics/opengl/Buffer.cpp @@ -35,18 +35,22 @@ namespace graphics namespace opengl { -Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) - : love::graphics::Buffer(size, type, usage, mapflags) - , vbo(0) - , memory_map(nullptr) - , modified_offset(0) - , modified_size(0) +Buffer::Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) + : love::graphics::Buffer(gfx, settings, format, size, arraylength) { - target = OpenGL::getGLBufferType(type); + size = getSize(); + arraylength = getArrayLength(); + + if (typeFlags & TYPEFLAG_VERTEX) + mapType = BUFFERTYPE_VERTEX; + else if (typeFlags & TYPEFLAG_INDEX) + mapType = BUFFERTYPE_INDEX; + + target = OpenGL::getGLBufferType(mapType); try { - memory_map = new char[size]; + memoryMap = new char[size]; } catch (std::bad_alloc &) { @@ -54,34 +58,63 @@ Buffer::Buffer(size_t size, const void *data, BufferType type, vertex::Usage usa } if (data != nullptr) - memcpy(memory_map, data, size); + memcpy(memoryMap, data, size); if (!load(data != nullptr)) { - delete[] memory_map; + delete[] memoryMap; throw love::Exception("Could not load vertex buffer (out of VRAM?)"); } } Buffer::~Buffer() { - if (vbo != 0) - unload(); + unloadVolatile(); + delete[] memoryMap; +} - delete[] memory_map; +bool Buffer::loadVolatile() +{ + return load(true); +} + +void Buffer::unloadVolatile() +{ + mapped = false; + if (vbo != 0) + gl.deleteBuffer(vbo); + vbo = 0; +} + +bool Buffer::load(bool restore) +{ + glGenBuffers(1, &vbo); + gl.bindBuffer(mapType, vbo); + + while (glGetError() != GL_NO_ERROR) + /* Clear the error buffer. */; + + // Copy the old buffer only if 'restore' was requested. + const GLvoid *src = restore ? memoryMap : nullptr; + + // Note that if 'src' is '0', no data will be copied. + glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage())); + + return (glGetError() == GL_NO_ERROR); } void *Buffer::map() { - if (is_mapped) - return memory_map; + if (mapped) + return memoryMap; - is_mapped = true; + mapped = true; - modified_offset = 0; - modified_size = 0; + modifiedOffset = 0; + modifiedSize = 0; + isMappedDataModified = false; - return memory_map; + return memoryMap; } void Buffer::unmapStatic(size_t offset, size_t size) @@ -90,8 +123,8 @@ void Buffer::unmapStatic(size_t offset, size_t size) return; // Upload the mapped data to the buffer. - gl.bindBuffer(type, vbo); - glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memory_map + offset); + gl.bindBuffer(mapType, vbo); + glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, memoryMap + offset); } void Buffer::unmapStream() @@ -100,87 +133,98 @@ void Buffer::unmapStream() // "orphan" current buffer to avoid implicit synchronisation on the GPU: // http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf - gl.bindBuffer(type, vbo); + gl.bindBuffer(mapType, vbo); glBufferData(target, (GLsizeiptr) getSize(), nullptr, glusage); #if LOVE_WINDOWS // TODO: Verify that this codepath is a useful optimization. if (gl.getVendor() == OpenGL::VENDOR_INTEL) - glBufferData(target, (GLsizeiptr) getSize(), memory_map, glusage); + glBufferData(target, (GLsizeiptr) getSize(), memoryMap, glusage); else #endif - glBufferSubData(target, 0, (GLsizeiptr) getSize(), memory_map); + glBufferSubData(target, 0, (GLsizeiptr) getSize(), memoryMap); } void Buffer::unmap() { - if (!is_mapped) + if (!mapped) return; - if ((map_flags & MAP_EXPLICIT_RANGE_MODIFY) != 0) + mapped = false; + + if ((mapFlags & MAP_EXPLICIT_RANGE_MODIFY) != 0) { - modified_offset = std::min(modified_offset, getSize() - 1); - modified_size = std::min(modified_size, getSize() - modified_offset); + if (!isMappedDataModified) + return; + + modifiedOffset = std::min(modifiedOffset, getSize() - 1); + modifiedSize = std::min(modifiedSize, getSize() - modifiedOffset); } else { - modified_offset = 0; - modified_size = getSize(); + modifiedOffset = 0; + modifiedSize = getSize(); } - if (modified_size > 0) + if (modifiedSize > 0) { switch (getUsage()) { - case vertex::USAGE_STATIC: - unmapStatic(modified_offset, modified_size); + case BUFFERUSAGE_STATIC: + unmapStatic(modifiedOffset, modifiedSize); break; - case vertex::USAGE_STREAM: + case BUFFERUSAGE_STREAM: unmapStream(); break; - case vertex::USAGE_DYNAMIC: + case BUFFERUSAGE_DYNAMIC: default: // It's probably more efficient to treat it like a streaming buffer if // at least a third of its contents have been modified during the map(). - if (modified_size >= getSize() / 3) + if (modifiedSize >= getSize() / 3) unmapStream(); else - unmapStatic(modified_offset, modified_size); + unmapStatic(modifiedOffset, modifiedSize); break; } } - modified_offset = 0; - modified_size = 0; - - is_mapped = false; + modifiedOffset = 0; + modifiedSize = 0; } void Buffer::setMappedRangeModified(size_t offset, size_t modifiedsize) { - if (!is_mapped || !(map_flags & MAP_EXPLICIT_RANGE_MODIFY)) + if (!mapped || !(mapFlags & MAP_EXPLICIT_RANGE_MODIFY)) return; + if (!isMappedDataModified) + { + modifiedOffset = offset; + modifiedSize = modifiedsize; + isMappedDataModified = true; + return; + } + // We're being conservative right now by internally marking the whole range // from the start of section a to the end of section b as modified if both // a and b are marked as modified. - size_t old_range_end = modified_offset + modified_size; - modified_offset = std::min(modified_offset, offset); + size_t oldrangeend = modifiedOffset + modifiedSize; + modifiedOffset = std::min(modifiedOffset, offset); - size_t new_range_end = std::max(offset + modifiedsize, old_range_end); - modified_size = new_range_end - modified_offset; + size_t newrangeend = std::max(offset + modifiedsize, oldrangeend); + modifiedSize = newrangeend - modifiedOffset; } void Buffer::fill(size_t offset, size_t size, const void *data) { - memcpy(memory_map + offset, data, size); + memcpy(memoryMap + offset, data, size); - if (is_mapped) + if (mapped) setMappedRangeModified(offset, size); else { - gl.bindBuffer(type, vbo); + gl.bindBuffer(mapType, vbo); glBufferSubData(target, (GLintptr) offset, (GLsizeiptr) size, data); } } @@ -192,41 +236,7 @@ ptrdiff_t Buffer::getHandle() const void Buffer::copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) { - other->fill(otheroffset, size, memory_map + offset); -} - -bool Buffer::loadVolatile() -{ - return load(true); -} - -void Buffer::unloadVolatile() -{ - unload(); -} - -bool Buffer::load(bool restore) -{ - glGenBuffers(1, &vbo); - gl.bindBuffer(type, vbo); - - while (glGetError() != GL_NO_ERROR) - /* Clear the error buffer. */; - - // Copy the old buffer only if 'restore' was requested. - const GLvoid *src = restore ? memory_map : nullptr; - - // Note that if 'src' is '0', no data will be copied. - glBufferData(target, (GLsizeiptr) getSize(), src, OpenGL::getGLBufferUsage(getUsage())); - - return (glGetError() == GL_NO_ERROR); -} - -void Buffer::unload() -{ - is_mapped = false; - gl.deleteBuffer(vbo); - vbo = 0; + other->fill(otheroffset, size, memoryMap + offset); } } // opengl diff --git a/src/modules/graphics/opengl/Buffer.h b/src/modules/graphics/opengl/Buffer.h index 195f0dfeb..64fde2f3c 100644 --- a/src/modules/graphics/opengl/Buffer.h +++ b/src/modules/graphics/opengl/Buffer.h @@ -32,6 +32,9 @@ namespace love { namespace graphics { + +class Graphics; + namespace opengl { @@ -39,9 +42,13 @@ class Buffer final : public love::graphics::Buffer, public Volatile { public: - Buffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags); + Buffer(love::graphics::Graphics *gfx, const Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength); virtual ~Buffer(); + // Implements Volatile. + bool loadVolatile() override; + void unloadVolatile() override; + void *map() override; void unmap() override; void setMappedRangeModified(size_t offset, size_t size) override; @@ -50,28 +57,25 @@ public: void copyTo(size_t offset, size_t size, love::graphics::Buffer *other, size_t otheroffset) override; - // Implements Volatile. - bool loadVolatile() override; - void unloadVolatile() override; - private: bool load(bool restore); - void unload(); void unmapStatic(size_t offset, size_t size); void unmapStream(); - GLenum target; + BufferType mapType = BUFFERTYPE_VERTEX; + GLenum target = 0; // The VBO identifier. Assigned by OpenGL. - GLuint vbo; + GLuint vbo = 0; // A pointer to mapped memory. - char *memory_map; + char *memoryMap = nullptr; - size_t modified_offset; - size_t modified_size; + size_t modifiedOffset = 0; + size_t modifiedSize = 0; + bool isMappedDataModified = false; }; // Buffer diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index a58952554..77e4f49e1 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -145,9 +145,9 @@ love::graphics::Shader *Graphics::newShaderInternal(love::graphics::ShaderStage return new Shader(vertex, pixel); } -love::graphics::Buffer *Graphics::newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) +love::graphics::Buffer *Graphics::newBuffer(const Buffer::Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) { - return new Buffer(size, data, type, usage, mapflags); + return new Buffer(this, settings, format, data, size, arraylength); } void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelheight) @@ -238,9 +238,9 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b { // Initial sizes that should be good enough for most cases. It will // resize to fit if needed, later. - batchedDrawState.vb[0] = CreateStreamBuffer(BUFFER_VERTEX, 1024 * 1024 * 1); - batchedDrawState.vb[1] = CreateStreamBuffer(BUFFER_VERTEX, 256 * 1024 * 1); - batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFER_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); + batchedDrawState.vb[0] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 1024 * 1024 * 1); + batchedDrawState.vb[1] = CreateStreamBuffer(BUFFERTYPE_VERTEX, 256 * 1024 * 1); + batchedDrawState.indexBuffer = CreateStreamBuffer(BUFFERTYPE_INDEX, sizeof(uint16) * LOVE_UINT16_MAX); } // Reload all volatile objects. @@ -360,7 +360,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd) GLenum glprimitivetype = OpenGL::getGLPrimitiveType(cmd.primitiveType); GLenum gldatatype = OpenGL::getGLIndexDataType(cmd.indexType); - gl.bindBuffer(BUFFER_INDEX, cmd.indexBuffer->getHandle()); + gl.bindBuffer(BUFFERTYPE_INDEX, cmd.indexBuffer->getHandle()); if (cmd.instanceCount > 1) glDrawElementsInstanced(glprimitivetype, cmd.indexCount, gldatatype, gloffset, cmd.instanceCount); @@ -370,13 +370,13 @@ void Graphics::draw(const DrawIndexedCommand &cmd) ++drawCalls; } -static inline void advanceVertexOffsets(const vertex::Attributes &attributes, vertex::BufferBindings &buffers, int vertexcount) +static inline void advanceVertexOffsets(const VertexAttributes &attributes, BufferBindings &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++) + for (unsigned int i = 0; i < VertexAttributes::MAX; i++) { if (!attributes.isEnabled(i)) continue; @@ -393,7 +393,7 @@ static inline void advanceVertexOffsets(const vertex::Attributes &attributes, ve } } -void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture) +void Graphics::drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &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; @@ -402,7 +402,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu gl.bindTextureToUnit(texture, 0, false); gl.setCullMode(CULL_NONE); - gl.bindBuffer(BUFFER_INDEX, quadIndexBuffer->getHandle()); + gl.bindBuffer(BUFFERTYPE_INDEX, quadIndexBuffer->getHandle()); if (gl.isBaseVertexSupported()) { @@ -422,7 +422,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu } else { - vertex::BufferBindings bufferscopy = buffers; + BufferBindings bufferscopy = buffers; if (start > 0) advanceVertexOffsets(attributes, bufferscopy, start * 4); @@ -510,7 +510,7 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, endPass(); bool iswindow = rts.getFirstTarget().texture == nullptr; - vertex::Winding vertexwinding = state.winding; + Winding vertexwinding = state.winding; if (iswindow) { @@ -528,10 +528,10 @@ void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, // Flip front face winding when rendering to a texture, since our // projection matrix is flipped. - vertexwinding = vertexwinding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW; + vertexwinding = vertexwinding == WINDING_CW ? WINDING_CCW : WINDING_CW; } - glFrontFace(vertexwinding == vertex::WINDING_CW ? GL_CW : GL_CCW); + glFrontFace(vertexwinding == WINDING_CW ? GL_CW : GL_CCW); gl.setViewport({0, 0, pixelw, pixelh}); @@ -1229,7 +1229,7 @@ void Graphics::setDepthMode(CompareMode compare, bool write) } } -void Graphics::setFrontFaceWinding(vertex::Winding winding) +void Graphics::setFrontFaceWinding(Winding winding) { DisplayState &state = states.back(); @@ -1239,9 +1239,9 @@ void Graphics::setFrontFaceWinding(vertex::Winding winding) state.winding = winding; if (isRenderTargetActive()) - winding = winding == vertex::WINDING_CW ? vertex::WINDING_CCW : vertex::WINDING_CW; + winding = winding == WINDING_CW ? WINDING_CCW : WINDING_CW; - glFrontFace(winding == vertex::WINDING_CW ? GL_CW : GL_CCW); + glFrontFace(winding == WINDING_CW ? GL_CW : GL_CCW); } void Graphics::setColor(Colorf c) diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index 6b8d51e54..d23934eb9 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -60,7 +60,7 @@ public: const char *getName() const override; love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override; - love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override; + love::graphics::Buffer *newBuffer(const Buffer::Settings &settings, const std::vector &format, const void *data, size_t size, size_t arraylength) override; void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override; bool setMode(int width, int height, int pixelwidth, int pixelheight, bool windowhasstencil) override; @@ -70,7 +70,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::BufferBindings &buffers, love::graphics::Texture *texture) override; + void drawQuads(int start, int count, const VertexAttributes &attributes, const BufferBindings &buffers, love::graphics::Texture *texture) override; void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override; void clear(const std::vector &colors, OptionalInt stencil, OptionalDouble depth) override; @@ -91,7 +91,7 @@ public: void setDepthMode(CompareMode compare, bool write) override; - void setFrontFaceWinding(vertex::Winding winding) override; + void setFrontFaceWinding(Winding winding) override; void setColorMask(ColorChannelMask mask) override; diff --git a/src/modules/graphics/opengl/OpenGL.cpp b/src/modules/graphics/opengl/OpenGL.cpp index 490d7b9b5..8fb8a497b 100644 --- a/src/modules/graphics/opengl/OpenGL.cpp +++ b/src/modules/graphics/opengl/OpenGL.cpp @@ -185,7 +185,7 @@ void OpenGL::setupContext() state.enabledAttribArrays = (uint32) ((1ull << uint32(maxvertexattribs)) - 1); state.instancedAttribArrays = 0; - setVertexAttributes(vertex::Attributes(), vertex::BufferBindings()); + setVertexAttributes(VertexAttributes(), BufferBindings()); // Get the current viewport. glGetIntegerv(GL_VIEWPORT, (GLint *) &state.viewport.x); @@ -222,7 +222,7 @@ void OpenGL::setupContext() glGetIntegerv(GL_CULL_FACE_MODE, &faceCull); state.faceCullMode = faceCull; - for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++) + for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++) { state.boundBuffers[i] = 0; glBindBuffer(getGLBufferType((BufferType) i), 0); @@ -562,16 +562,11 @@ GLenum OpenGL::getGLPrimitiveType(PrimitiveType type) { switch (type) { - case PRIMITIVE_TRIANGLES: - return GL_TRIANGLES; - case PRIMITIVE_TRIANGLE_STRIP: - return GL_TRIANGLE_STRIP; - case PRIMITIVE_TRIANGLE_FAN: - return GL_TRIANGLE_FAN; - case PRIMITIVE_POINTS: - return GL_POINTS; - case PRIMITIVE_MAX_ENUM: - return GL_ZERO; + case PRIMITIVE_TRIANGLES: return GL_TRIANGLES; + case PRIMITIVE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + case PRIMITIVE_TRIANGLE_FAN: return GL_TRIANGLE_FAN; + case PRIMITIVE_POINTS: return GL_POINTS; + case PRIMITIVE_MAX_ENUM: return GL_ZERO; } return GL_ZERO; @@ -581,12 +576,9 @@ GLenum OpenGL::getGLBufferType(BufferType type) { switch (type) { - case BUFFER_VERTEX: - return GL_ARRAY_BUFFER; - case BUFFER_INDEX: - return GL_ELEMENT_ARRAY_BUFFER; - case BUFFER_MAX_ENUM: - return GL_ZERO; + case BUFFERTYPE_VERTEX: return GL_ARRAY_BUFFER; + case BUFFERTYPE_INDEX: return GL_ELEMENT_ARRAY_BUFFER; + case BUFFERTYPE_MAX_ENUM: return GL_ZERO; } return GL_ZERO; @@ -596,16 +588,11 @@ GLenum OpenGL::getGLTextureType(TextureType type) { switch (type) { - case TEXTURE_2D: - return GL_TEXTURE_2D; - case TEXTURE_VOLUME: - return GL_TEXTURE_3D; - case TEXTURE_2D_ARRAY: - return GL_TEXTURE_2D_ARRAY; - case TEXTURE_CUBE: - return GL_TEXTURE_CUBE_MAP; - case TEXTURE_MAX_ENUM: - return GL_ZERO; + case TEXTURE_2D: return GL_TEXTURE_2D; + case TEXTURE_VOLUME: return GL_TEXTURE_3D; + case TEXTURE_2D_ARRAY: return GL_TEXTURE_2D_ARRAY; + case TEXTURE_CUBE: return GL_TEXTURE_CUBE_MAP; + case TEXTURE_MAX_ENUM: return GL_ZERO; } return GL_ZERO; @@ -615,74 +602,159 @@ GLenum OpenGL::getGLIndexDataType(IndexDataType type) { switch (type) { - case INDEX_UINT16: - return GL_UNSIGNED_SHORT; - case INDEX_UINT32: - return GL_UNSIGNED_INT; - default: - return GL_ZERO; + case INDEX_UINT16: return GL_UNSIGNED_SHORT; + case INDEX_UINT32: return GL_UNSIGNED_INT; + default: return GL_ZERO; } } -GLenum OpenGL::getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat) +GLenum OpenGL::getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat) { normalized = GL_FALSE; intformat = false; + components = 1; - switch (type) + switch (format) { - case vertex::DATA_SNORM8: - normalized = GL_TRUE; - return GL_BYTE; - case vertex::DATA_UNORM8: - normalized = GL_TRUE; - return GL_UNSIGNED_BYTE; - case vertex::DATA_INT8: - intformat = true; - return GL_BYTE; - case vertex::DATA_UINT8: - intformat = true; - return GL_UNSIGNED_BYTE; - case vertex::DATA_SNORM16: - normalized = GL_TRUE; - return GL_SHORT; - case vertex::DATA_UNORM16: - normalized = GL_TRUE; - return GL_UNSIGNED_SHORT; - case vertex::DATA_INT16: - intformat = true; - return GL_SHORT; - case vertex::DATA_UINT16: - intformat = true; - return GL_UNSIGNED_SHORT; - case vertex::DATA_INT32: + case DATAFORMAT_FLOAT: + components = 1; + return GL_FLOAT; + case DATAFORMAT_FLOAT_VEC2: + components = 2; + return GL_FLOAT; + case DATAFORMAT_FLOAT_VEC3: + components = 3; + return GL_FLOAT; + case DATAFORMAT_FLOAT_VEC4: + components = 4; + return GL_FLOAT; + + case DATAFORMAT_FLOAT_MAT2X2: + case DATAFORMAT_FLOAT_MAT2X3: + case DATAFORMAT_FLOAT_MAT2X4: + case DATAFORMAT_FLOAT_MAT3X2: + case DATAFORMAT_FLOAT_MAT3X3: + case DATAFORMAT_FLOAT_MAT3X4: + case DATAFORMAT_FLOAT_MAT4X2: + case DATAFORMAT_FLOAT_MAT4X3: + case DATAFORMAT_FLOAT_MAT4X4: + return GL_ZERO; + + case DATAFORMAT_INT32: + components = 1; intformat = true; return GL_INT; - case vertex::DATA_UINT32: + case DATAFORMAT_INT32_VEC2: + components = 2; + intformat = true; + return GL_INT; + case DATAFORMAT_INT32_VEC3: + components = 3; + intformat = true; + return GL_INT; + case DATAFORMAT_INT32_VEC4: + components = 4; + intformat = true; + return GL_INT; + + case DATAFORMAT_UINT32: + components = 1; intformat = true; return GL_UNSIGNED_INT; - case vertex::DATA_FLOAT: - normalized = GL_FALSE; - return GL_FLOAT; - case vertex::DATA_MAX_ENUM: + case DATAFORMAT_UINT32_VEC2: + components = 2; + intformat = true; + return GL_UNSIGNED_INT; + case DATAFORMAT_UINT32_VEC3: + components = 3; + intformat = true; + return GL_UNSIGNED_INT; + case DATAFORMAT_UINT32_VEC4: + components = 4; + intformat = true; + return GL_UNSIGNED_INT; + + case DATAFORMAT_SNORM8_VEC4: + components = 4; + normalized = GL_TRUE; + return GL_BYTE; + + case DATAFORMAT_UNORM8_VEC4: + components = 4; + normalized = GL_TRUE; + return GL_UNSIGNED_BYTE; + + case DATAFORMAT_INT8_VEC4: + components = 4; + intformat = true; + return GL_BYTE; + + case DATAFORMAT_UINT8_VEC4: + components = 4; + intformat = true; + return GL_UNSIGNED_BYTE; + + case DATAFORMAT_SNORM16_VEC2: + components = 2; + normalized = GL_TRUE; + return GL_BYTE; + case DATAFORMAT_SNORM16_VEC4: + components = 4; + normalized = GL_TRUE; + return GL_BYTE; + + case DATAFORMAT_UNORM16_VEC2: + components = 2; + normalized = GL_TRUE; + return GL_UNSIGNED_SHORT; + case DATAFORMAT_UNORM16_VEC4: + components = 4; + normalized = GL_TRUE; + return GL_UNSIGNED_SHORT; + + case DATAFORMAT_INT16_VEC2: + components = 2; + intformat = true; + return GL_SHORT; + case DATAFORMAT_INT16_VEC4: + components = 4; + intformat = true; + return GL_SHORT; + + case DATAFORMAT_UINT16: + components = 1; + intformat = true; + return GL_UNSIGNED_SHORT; + case DATAFORMAT_UINT16_VEC2: + components = 2; + intformat = true; + return GL_UNSIGNED_SHORT; + case DATAFORMAT_UINT16_VEC4: + components = 4; + intformat = true; + return GL_UNSIGNED_SHORT; + + case DATAFORMAT_BOOL: + case DATAFORMAT_BOOL_VEC2: + case DATAFORMAT_BOOL_VEC3: + case DATAFORMAT_BOOL_VEC4: + return GL_ZERO; + + case DATAFORMAT_MAX_ENUM: return GL_ZERO; } return GL_ZERO; } -GLenum OpenGL::getGLBufferUsage(vertex::Usage usage) +GLenum OpenGL::getGLBufferUsage(BufferUsage usage) { switch (usage) { - case vertex::USAGE_STREAM: - return GL_STREAM_DRAW; - case vertex::USAGE_DYNAMIC: - return GL_DYNAMIC_DRAW; - case vertex::USAGE_STATIC: - return GL_STATIC_DRAW; - default: - return 0; + case BUFFERUSAGE_STREAM: return GL_STREAM_DRAW; + case BUFFERUSAGE_DYNAMIC: return GL_DYNAMIC_DRAW; + case BUFFERUSAGE_STATIC: return GL_STATIC_DRAW; + default: return 0; } } @@ -699,14 +771,14 @@ void OpenGL::deleteBuffer(GLuint buffer) { glDeleteBuffers(1, &buffer); - for (int i = 0; i < (int) BUFFER_MAX_ENUM; i++) + for (int i = 0; i < (int) BUFFERTYPE_MAX_ENUM; i++) { if (state.boundBuffers[i] == buffer) state.boundBuffers[i] = 0; } } -void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers) +void OpenGL::setVertexAttributes(const VertexAttributes &attributes, const BufferBindings &buffers) { uint32 enablediff = attributes.enableBits ^ state.enabledAttribArrays; uint32 instanceattribbits = 0; @@ -739,18 +811,19 @@ void OpenGL::setVertexAttributes(const vertex::Attributes &attributes, const ver if ((state.instancedAttribArrays & bit) ^ divisorbit) glVertexAttribDivisor(i, divisor); + int components = 0; GLboolean normalized = GL_FALSE; bool intformat = false; - GLenum gltype = getGLVertexDataType(attrib.type, normalized, intformat); + GLenum gltype = getGLVertexDataType(attrib.format, components, normalized, intformat); const void *offsetpointer = reinterpret_cast(bufferinfo.offset + attrib.offsetFromVertex); - bindBuffer(BUFFER_VERTEX, (GLuint) bufferinfo.buffer->getHandle()); + bindBuffer(BUFFERTYPE_VERTEX, (GLuint) bufferinfo.buffer->getHandle()); if (intformat) - glVertexAttribIPointer(i, attrib.components, gltype, layout.stride, offsetpointer); + glVertexAttribIPointer(i, components, gltype, layout.stride, offsetpointer); else - glVertexAttribPointer(i, attrib.components, gltype, normalized, layout.stride, offsetpointer); + glVertexAttribPointer(i, components, gltype, normalized, layout.stride, offsetpointer); } i++; @@ -1073,24 +1146,15 @@ GLint OpenGL::getGLCompareMode(CompareMode mode) { switch (mode) { - case COMPARE_LESS: - return GL_LESS; - case COMPARE_LEQUAL: - return GL_LEQUAL; - case COMPARE_EQUAL: - return GL_EQUAL; - case COMPARE_GEQUAL: - return GL_GEQUAL; - case COMPARE_GREATER: - return GL_GREATER; - case COMPARE_NOTEQUAL: - return GL_NOTEQUAL; - case COMPARE_ALWAYS: - return GL_ALWAYS; - case COMPARE_NEVER: - return GL_NEVER; - default: - return GL_NEVER; + case COMPARE_LESS: return GL_LESS; + case COMPARE_LEQUAL: return GL_LEQUAL; + case COMPARE_EQUAL: return GL_EQUAL; + case COMPARE_GEQUAL: return GL_GEQUAL; + case COMPARE_GREATER: return GL_GREATER; + case COMPARE_NOTEQUAL: return GL_NOTEQUAL; + case COMPARE_ALWAYS: return GL_ALWAYS; + case COMPARE_NEVER: return GL_NEVER; + default: return GL_NEVER; } } diff --git a/src/modules/graphics/opengl/OpenGL.h b/src/modules/graphics/opengl/OpenGL.h index 3a57a2d55..a16d20313 100644 --- a/src/modules/graphics/opengl/OpenGL.h +++ b/src/modules/graphics/opengl/OpenGL.h @@ -238,7 +238,7 @@ public: /** * Set all vertex attribute state. **/ - void setVertexAttributes(const vertex::Attributes &attributes, const vertex::BufferBindings &buffers); + void setVertexAttributes(const VertexAttributes &attributes, const BufferBindings &buffers); /** * Wrapper for glCullFace which eliminates redundant state setting. @@ -399,8 +399,8 @@ public: static GLenum getGLPrimitiveType(PrimitiveType type); static GLenum getGLBufferType(BufferType type); static GLenum getGLIndexDataType(IndexDataType type); - static GLenum getGLVertexDataType(vertex::DataType type, GLboolean &normalized, bool &intformat); - static GLenum getGLBufferUsage(vertex::Usage usage); + static GLenum getGLVertexDataType(DataFormat format, int &components, GLboolean &normalized, bool &intformat); + static GLenum getGLBufferUsage(BufferUsage usage); static GLenum getGLTextureType(TextureType type); static GLint getGLWrapMode(SamplerState::WrapMode wmode); static GLint getGLCompareMode(CompareMode mode); @@ -448,7 +448,7 @@ private: // Tracked OpenGL state. struct { - GLuint boundBuffers[BUFFER_MAX_ENUM]; + GLuint boundBuffers[BUFFERTYPE_MAX_ENUM]; // Texture unit state (currently bound texture for each texture unit.) std::vector boundTextures[TEXTURE_MAX_ENUM]; diff --git a/src/modules/graphics/opengl/Shader.cpp b/src/modules/graphics/opengl/Shader.cpp index 67a459d7b..f93f126b9 100644 --- a/src/modules/graphics/opengl/Shader.cpp +++ b/src/modules/graphics/opengl/Shader.cpp @@ -23,6 +23,7 @@ #include "Shader.h" #include "Graphics.h" +#include "graphics/vertex.h" // C++ #include @@ -322,7 +323,7 @@ bool Shader::loadVolatile() for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++) { const char *name = nullptr; - if (vertex::getConstant((BuiltinVertexAttribute) i, name)) + if (graphics::getConstant((BuiltinVertexAttribute) i, name)) glBindAttribLocation(program, i, (const GLchar *) name); } @@ -345,7 +346,7 @@ bool Shader::loadVolatile() for (int i = 0; i < int(ATTRIB_MAX_ENUM); i++) { const char *name = nullptr; - if (vertex::getConstant(BuiltinVertexAttribute(i), name)) + if (graphics::getConstant(BuiltinVertexAttribute(i), name)) builtinAttributes[i] = glGetAttribLocation(program, name); else builtinAttributes[i] = -1; diff --git a/src/modules/graphics/vertex.cpp b/src/modules/graphics/vertex.cpp index e979af478..ffc3e448c 100644 --- a/src/modules/graphics/vertex.cpp +++ b/src/modules/graphics/vertex.cpp @@ -25,8 +25,6 @@ namespace love { namespace graphics { -namespace vertex -{ static_assert(sizeof(Color32) == 4, "sizeof(Color32) incorrect!"); static_assert(sizeof(STf_RGBAub) == sizeof(float)*2 + sizeof(Color32), "sizeof(STf_RGBAub) incorrect!"); @@ -103,6 +101,68 @@ int getFormatPositionComponents(CommonFormat format) return 0; } +// Order here relies on order of DataFormat enum. +static const DataFormatInfo dataFormatInfo[] +{ + // baseType, isMatrix, components, rows, columns, componentSize, size + { DATA_BASETYPE_FLOAT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_FLOAT + { DATA_BASETYPE_FLOAT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_FLOAT_VEC2 + { DATA_BASETYPE_FLOAT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_FLOAT_VEC3 + { DATA_BASETYPE_FLOAT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_FLOAT_VEC4 + + { DATA_BASETYPE_FLOAT, true, 0, 2, 2, 4, 16 }, // DATAFORMAT_FLOAT_MAT2X2 + { DATA_BASETYPE_FLOAT, true, 0, 2, 3, 4, 24 }, // DATAFORMAT_FLOAT_MAT2X3 + { DATA_BASETYPE_FLOAT, true, 0, 2, 4, 4, 32 }, // DATAFORMAT_FLOAT_MAT2X4 + + { DATA_BASETYPE_FLOAT, true, 0, 3, 2, 4, 24 }, // DATAFORMAT_FLOAT_MAT3X2 + { DATA_BASETYPE_FLOAT, true, 0, 3, 3, 4, 36 }, // DATAFORMAT_FLOAT_MAT3X3 + { DATA_BASETYPE_FLOAT, true, 0, 3, 4, 4, 48 }, // DATAFORMAT_FLOAT_MAT3X4 + + { DATA_BASETYPE_FLOAT, true, 0, 4, 2, 4, 32 }, // DATAFORMAT_FLOAT_MAT4X2 + { DATA_BASETYPE_FLOAT, true, 0, 4, 3, 4, 48 }, // DATAFORMAT_FLOAT_MAT4X3 + { DATA_BASETYPE_FLOAT, true, 0, 4, 4, 4, 64 }, // DATAFORMAT_FLOAT_MAT4X4 + + { DATA_BASETYPE_INT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_INT32 + { DATA_BASETYPE_INT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_INT32_VEC2 + { DATA_BASETYPE_INT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_INT32_VEC3 + { DATA_BASETYPE_INT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_INT32_VEC4 + + { DATA_BASETYPE_UINT, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_UINT32 + { DATA_BASETYPE_UINT, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_UINT32_VEC2 + { DATA_BASETYPE_UINT, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_UINT32_VEC3 + { DATA_BASETYPE_UINT, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_UINT32_VEC4 + + { DATA_BASETYPE_SNORM, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_SNORM8_VEC4 + { DATA_BASETYPE_UNORM, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_UNORM8_VEC4 + { DATA_BASETYPE_INT, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_INT8_VEC4 + { DATA_BASETYPE_UINT, false, 4, 0, 0, 1, 4 }, // DATAFORMAT_UINT8_VEC4 + + { DATA_BASETYPE_SNORM, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_SNORM16_VEC2 + { DATA_BASETYPE_SNORM, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_SNORM16_VEC4 + + { DATA_BASETYPE_UNORM, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_UNORM16_VEC2 + { DATA_BASETYPE_UNORM, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_UNORM16_VEC4 + + { DATA_BASETYPE_INT, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_INT16_VEC2 + { DATA_BASETYPE_INT, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_INT16_VEC4 + + { DATA_BASETYPE_UINT, false, 1, 0, 0, 2, 2 }, // DATAFORMAT_UINT16 + { DATA_BASETYPE_UINT, false, 2, 0, 0, 2, 4 }, // DATAFORMAT_UINT16_VEC2 + { DATA_BASETYPE_UINT, false, 4, 0, 0, 2, 8 }, // DATAFORMAT_UINT16_VEC4 + + { DATA_BASETYPE_BOOL, false, 1, 0, 0, 4, 4 }, // DATAFORMAT_BOOL + { DATA_BASETYPE_BOOL, false, 2, 0, 0, 4, 8 }, // DATAFORMAT_BOOL_VEC2 + { DATA_BASETYPE_BOOL, false, 3, 0, 0, 4, 12 }, // DATAFORMAT_BOOL_VEC3 + { DATA_BASETYPE_BOOL, false, 4, 0, 0, 4, 16 }, // DATAFORMAT_BOOL_VEC4 +}; + +static_assert((sizeof(dataFormatInfo) / sizeof(DataFormatInfo)) == DATAFORMAT_MAX_ENUM, "dataFormatInfo array size must match number of DataFormat enum values."); + +const DataFormatInfo &getDataFormatInfo(DataFormat format) +{ + return dataFormatInfo[format]; +} + size_t getIndexDataSize(IndexDataType type) { switch (type) @@ -113,63 +173,36 @@ size_t getIndexDataSize(IndexDataType type) } } -size_t getDataTypeSize(DataType datatype) -{ - switch (datatype) - { - case DATA_SNORM8: - case DATA_UNORM8: - case DATA_INT8: - case DATA_UINT8: - return sizeof(uint8); - case DATA_SNORM16: - case DATA_UNORM16: - case DATA_INT16: - case DATA_UINT16: - return sizeof(uint16); - case DATA_INT32: - case DATA_UINT32: - return sizeof(uint32); - case DATA_FLOAT: - return sizeof(float); - case DATA_MAX_ENUM: - return 0; - } - return 0; -} - -bool isDataTypeInteger(DataType datatype) -{ - switch (datatype) - { - case DATA_INT8: - case DATA_UINT8: - case DATA_INT16: - case DATA_UINT16: - case DATA_INT32: - case DATA_UINT32: - return true; - default: - return false; - } -} - IndexDataType getIndexDataTypeFromMax(size_t maxvalue) { - IndexDataType types[] = {INDEX_UINT16, INDEX_UINT32}; - return types[maxvalue > LOVE_UINT16_MAX ? 1 : 0]; + return maxvalue > LOVE_UINT16_MAX ? INDEX_UINT32 : INDEX_UINT16; +} + +DataFormat getIndexDataFormat(IndexDataType type) +{ + return type == INDEX_UINT32 ? DATAFORMAT_UINT32 : DATAFORMAT_UINT16; +} + +IndexDataType getIndexDataType(DataFormat format) +{ + switch (format) + { + case DATAFORMAT_UINT16: return INDEX_UINT16; + case DATAFORMAT_UINT32: return INDEX_UINT32; + default: return INDEX_MAX_ENUM; + } } int getIndexCount(TriangleIndexMode mode, int vertexCount) { switch (mode) { - case TriangleIndexMode::NONE: + case TRIANGLEINDEX_NONE: return 0; - case TriangleIndexMode::STRIP: - case TriangleIndexMode::FAN: + case TRIANGLEINDEX_STRIP: + case TRIANGLEINDEX_FAN: return 3 * (vertexCount - 2); - case TriangleIndexMode::QUADS: + case TRIANGLEINDEX_QUADS: return vertexCount * 6 / 4; } return 0; @@ -180,9 +213,9 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T { switch (mode) { - case TriangleIndexMode::NONE: + case TRIANGLEINDEX_NONE: break; - case TriangleIndexMode::STRIP: + case TRIANGLEINDEX_STRIP: { int i = 0; for (T index = 0; index < vertexCount - 2; index++) @@ -193,7 +226,7 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T } } break; - case TriangleIndexMode::FAN: + case TRIANGLEINDEX_FAN: { int i = 0; for (T index = 2; index < vertexCount; index++) @@ -204,7 +237,7 @@ static void fillIndicesT(TriangleIndexMode mode, T vertexStart, T vertexCount, T } } break; - case TriangleIndexMode::QUADS: + case TRIANGLEINDEX_QUADS: { // 0---2 // | / | @@ -238,7 +271,7 @@ void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, fillIndicesT(mode, vertexStart, vertexCount, indices); } -void Attributes::setCommonFormat(CommonFormat format, uint8 bufferindex) +void VertexAttributes::setCommonFormat(CommonFormat format, uint8 bufferindex) { setBufferLayout(bufferindex, (uint16) getFormatStride(format)); @@ -247,241 +280,180 @@ void Attributes::setCommonFormat(CommonFormat format, uint8 bufferindex) case CommonFormat::NONE: break; case CommonFormat::XYf: - set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex); + set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex); break; case CommonFormat::XYZf: - set(ATTRIB_POS, DATA_FLOAT, 3, 0, bufferindex); + set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC3, 0, bufferindex); break; case CommonFormat::RGBAub: - set(ATTRIB_COLOR, DATA_UNORM8, 4, 0, bufferindex); + set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, 0, bufferindex); break; case CommonFormat::STf_RGBAub: - set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, 0, bufferindex); - set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2), bufferindex); + set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, 0, bufferindex); + set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 2), bufferindex); break; case CommonFormat::STPf_RGBAub: - set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, 0, bufferindex); - set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 3), bufferindex); + set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, 0, bufferindex); + set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 3), bufferindex); break; case CommonFormat::XYf_STf: - set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex); - set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), bufferindex); + set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex); + set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, uint16(sizeof(float) * 2), bufferindex); break; case CommonFormat::XYf_STPf: - set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex); - set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), bufferindex); + set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex); + set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, uint16(sizeof(float) * 2), bufferindex); break; case CommonFormat::XYf_STf_RGBAub: - set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex); - set(ATTRIB_TEXCOORD, DATA_FLOAT, 2, uint16(sizeof(float) * 2), bufferindex); - set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 4), bufferindex); + set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex); + set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC2, uint16(sizeof(float) * 2), bufferindex); + set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 4), bufferindex); break; case CommonFormat::XYf_STus_RGBAub: - set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex); - set(ATTRIB_TEXCOORD, DATA_UNORM16, 2, uint16(sizeof(float) * 2), bufferindex); - set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), bufferindex); + set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex); + set(ATTRIB_TEXCOORD, DATAFORMAT_UNORM16_VEC2, uint16(sizeof(float) * 2), bufferindex); + set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 2 + sizeof(uint16) * 2), bufferindex); break; case CommonFormat::XYf_STPf_RGBAub: - set(ATTRIB_POS, DATA_FLOAT, 2, 0, bufferindex); - set(ATTRIB_TEXCOORD, DATA_FLOAT, 3, uint16(sizeof(float) * 2), bufferindex); - set(ATTRIB_COLOR, DATA_UNORM8, 4, uint16(sizeof(float) * 5), bufferindex); + set(ATTRIB_POS, DATAFORMAT_FLOAT_VEC2, 0, bufferindex); + set(ATTRIB_TEXCOORD, DATAFORMAT_FLOAT_VEC3, uint16(sizeof(float) * 2), bufferindex); + set(ATTRIB_COLOR, DATAFORMAT_UNORM8_VEC4, uint16(sizeof(float) * 5), bufferindex); break; } } -static StringMap::Entry attribNameEntries[] = +STRINGMAP_BEGIN(BuiltinVertexAttribute, ATTRIB_MAX_ENUM, attribName) { { "VertexPosition", ATTRIB_POS }, { "VertexTexCoord", ATTRIB_TEXCOORD }, { "VertexColor", ATTRIB_COLOR }, -}; +} +STRINGMAP_END(BuiltinVertexAttribute, ATTRIB_MAX_ENUM, attribName) -static StringMap attribNames(attribNameEntries, sizeof(attribNameEntries)); +const char *getConstant(BuiltinVertexAttribute attrib) +{ + const char *name = nullptr; + getConstant(attrib, name); + return name; +} -static StringMap::Entry indexTypeEntries[] = +STRINGMAP_BEGIN(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName) +{ + { "vertex", BUFFERTYPE_VERTEX }, + { "index", BUFFERTYPE_INDEX }, +} +STRINGMAP_END(BufferType, BUFFERTYPE_MAX_ENUM, bufferTypeName) + +STRINGMAP_BEGIN(IndexDataType, INDEX_MAX_ENUM, indexType) { { "uint16", INDEX_UINT16 }, { "uint32", INDEX_UINT32 }, -}; +} +STRINGMAP_END(IndexDataType, INDEX_MAX_ENUM, indexType) -static StringMap indexTypes(indexTypeEntries, sizeof(indexTypeEntries)); - -static StringMap::Entry usageEntries[] = +STRINGMAP_BEGIN(BufferUsage, BUFFERUSAGE_MAX_ENUM, bufferUsage) { - { "stream", USAGE_STREAM }, - { "dynamic", USAGE_DYNAMIC }, - { "static", USAGE_STATIC }, -}; + { "stream", BUFFERUSAGE_STREAM }, + { "dynamic", BUFFERUSAGE_DYNAMIC }, + { "static", BUFFERUSAGE_STATIC }, +} +STRINGMAP_END(BufferUsage, BUFFERUSAGE_MAX_ENUM, bufferUsage) -static StringMap usages(usageEntries, sizeof(usageEntries)); - -static StringMap::Entry primitiveTypeEntries[] = +STRINGMAP_BEGIN(PrimitiveType, PRIMITIVE_MAX_ENUM, primitiveType) { { "fan", PRIMITIVE_TRIANGLE_FAN }, { "strip", PRIMITIVE_TRIANGLE_STRIP }, { "triangles", PRIMITIVE_TRIANGLES }, { "points", PRIMITIVE_POINTS }, -}; +} +STRINGMAP_END(PrimitiveType, PRIMITIVE_MAX_ENUM, primitiveType) -static StringMap primitiveTypes(primitiveTypeEntries, sizeof(primitiveTypeEntries)); - -static StringMap::Entry attributeStepEntries[] = +STRINGMAP_BEGIN(AttributeStep, STEP_MAX_ENUM, attributeStep) { { "pervertex", STEP_PER_VERTEX }, { "perinstance", STEP_PER_INSTANCE }, -}; +} +STRINGMAP_END(AttributeStep, STEP_MAX_ENUM, attributeStep) -static StringMap attributeSteps(attributeStepEntries, sizeof(attributeStepEntries)); - -static StringMap::Entry dataTypeEntries[] = +STRINGMAP_BEGIN(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat) { - { "snorm8", DATA_SNORM8 }, - { "unorm8", DATA_UNORM8 }, - { "int8", DATA_INT8 }, - { "uint8", DATA_UINT8 }, - { "snorm16", DATA_SNORM16 }, - { "unorm16", DATA_UNORM16 }, - { "int16", DATA_INT16 }, - { "uint16", DATA_UINT16 }, - { "int32", DATA_INT32 }, - { "uint32", DATA_UINT32 }, - { "float", DATA_FLOAT }, -}; + { "float", DATAFORMAT_FLOAT }, + { "floatvec2", DATAFORMAT_FLOAT_VEC2 }, + { "floatvec3", DATAFORMAT_FLOAT_VEC3 }, + { "floatvec4", DATAFORMAT_FLOAT_VEC4 }, -static StringMap dataTypes(dataTypeEntries, sizeof(dataTypeEntries)); + { "floatmat2x2", DATAFORMAT_FLOAT_MAT2X2 }, + { "floatmat2x3", DATAFORMAT_FLOAT_MAT2X3 }, + { "floatmat2x4", DATAFORMAT_FLOAT_MAT2X4 }, -static StringMap::Entry cullModeEntries[] = + { "floatmat3x2", DATAFORMAT_FLOAT_MAT3X2 }, + { "floatmat3x3", DATAFORMAT_FLOAT_MAT3X3 }, + { "floatmat3x4", DATAFORMAT_FLOAT_MAT3X4 }, + + { "floatmat4x2", DATAFORMAT_FLOAT_MAT4X2 }, + { "floatmat4x3", DATAFORMAT_FLOAT_MAT4X3 }, + { "floatmat4x4", DATAFORMAT_FLOAT_MAT4X4 }, + + { "int32", DATAFORMAT_INT32 }, + { "int32vec2", DATAFORMAT_INT32_VEC2 }, + { "int32vec3", DATAFORMAT_INT32_VEC3 }, + { "int32vec4", DATAFORMAT_INT32_VEC4 }, + + { "uint32", DATAFORMAT_UINT32 }, + { "uint32vec2", DATAFORMAT_UINT32_VEC2 }, + { "uint32vec3", DATAFORMAT_UINT32_VEC3 }, + { "uint32vec4", DATAFORMAT_UINT32_VEC4 }, + + { "snorm8vec4", DATAFORMAT_SNORM8_VEC4 }, + { "unorm8vec4", DATAFORMAT_UNORM8_VEC4 }, + { "int8vec4", DATAFORMAT_INT8_VEC4 }, + { "uint8vec4", DATAFORMAT_UINT8_VEC4 }, + + { "snorm16vec2", DATAFORMAT_SNORM16_VEC2 }, + { "snorm16vec4", DATAFORMAT_SNORM16_VEC4 }, + + { "unorm16vec2", DATAFORMAT_UNORM16_VEC2 }, + { "unorm16vec4", DATAFORMAT_UNORM16_VEC4 }, + + { "int16vec2", DATAFORMAT_INT16_VEC2 }, + { "int16vec4", DATAFORMAT_INT16_VEC4 }, + + { "uint16", DATAFORMAT_UINT16 }, + { "uint16vec2", DATAFORMAT_UINT16_VEC2 }, + { "uint16vec4", DATAFORMAT_UINT16_VEC4 }, + + { "bool", DATAFORMAT_BOOL }, + { "boolvec2", DATAFORMAT_BOOL_VEC2 }, + { "boolvec3", DATAFORMAT_BOOL_VEC3 }, + { "boolvec4", DATAFORMAT_BOOL_VEC4 }, +} +STRINGMAP_END(DataFormat, DATAFORMAT_MAX_ENUM, dataFormat) + +STRINGMAP_BEGIN(DataBaseType, DATA_BASETYPE_MAX_ENUM, dataBaseType) +{ + { "float", DATA_BASETYPE_FLOAT }, + { "int", DATA_BASETYPE_INT }, + { "uint", DATA_BASETYPE_UINT }, + { "snorm", DATA_BASETYPE_SNORM }, + { "unorm", DATA_BASETYPE_UNORM }, + { "bool", DATA_BASETYPE_BOOL }, +} +STRINGMAP_END(DataBaseType, DATA_BASETYPE_MAX_ENUM, dataBaseType) + +STRINGMAP_BEGIN(CullMode, CULL_MAX_ENUM, cullMode) { { "none", CULL_NONE }, { "back", CULL_BACK }, { "front", CULL_FRONT }, -}; +} +STRINGMAP_END(CullMode, CULL_MAX_ENUM, cullMode) -static StringMap cullModes(cullModeEntries, sizeof(cullModeEntries)); - -static StringMap::Entry windingEntries[] = +STRINGMAP_BEGIN(Winding, WINDING_MAX_ENUM, winding) { { "cw", WINDING_CW }, { "ccw", WINDING_CCW }, -}; - -static StringMap windings(windingEntries, sizeof(windingEntries)); - -bool getConstant(const char *in, BuiltinVertexAttribute &out) -{ - return attribNames.find(in, out); } +STRINGMAP_END(Winding, WINDING_MAX_ENUM, winding) -bool getConstant(BuiltinVertexAttribute in, const char *&out) -{ - return attribNames.find(in, out); -} - -bool getConstant(const char *in, IndexDataType &out) -{ - return indexTypes.find(in, out); -} - -bool getConstant(IndexDataType in, const char *&out) -{ - return indexTypes.find(in, out); -} - -std::vector getConstants(IndexDataType) -{ - return indexTypes.getNames(); -} - -bool getConstant(const char *in, Usage &out) -{ - return usages.find(in, out); -} - -bool getConstant(Usage in, const char *&out) -{ - return usages.find(in, out); -} - -std::vector getConstants(Usage) -{ - return usages.getNames(); -} - -bool getConstant(const char *in, PrimitiveType &out) -{ - return primitiveTypes.find(in, out); -} - -bool getConstant(PrimitiveType in, const char *&out) -{ - return primitiveTypes.find(in, out); -} - -std::vector getConstants(PrimitiveType) -{ - return primitiveTypes.getNames(); -} - -bool getConstant(const char *in, AttributeStep &out) -{ - return attributeSteps.find(in, out); -} - -bool getConstant(AttributeStep in, const char *&out) -{ - return attributeSteps.find(in, out); -} - -std::vector getConstants(AttributeStep) -{ - return attributeSteps.getNames(); -} - -bool getConstant(const char *in, DataType &out) -{ - return dataTypes.find(in, out); -} - -bool getConstant(DataType in, const char *&out) -{ - return dataTypes.find(in, out); -} - -std::vector getConstants(DataType) -{ - return dataTypes.getNames(); -} - -bool getConstant(const char *in, CullMode &out) -{ - return cullModes.find(in, out); -} - -bool getConstant(CullMode in, const char *&out) -{ - return cullModes.find(in, out); -} - -std::vector getConstants(CullMode) -{ - return cullModes.getNames(); -} - -bool getConstant(const char *in, Winding &out) -{ - return windings.find(in, out); -} - -bool getConstant(Winding in, const char *&out) -{ - return windings.find(in, out); -} - -std::vector getConstants(Winding) -{ - return windings.getNames(); -} - -} // vertex } // graphics } // love diff --git a/src/modules/graphics/vertex.h b/src/modules/graphics/vertex.h index 098f20e59..5dd74ce47 100644 --- a/src/modules/graphics/vertex.h +++ b/src/modules/graphics/vertex.h @@ -23,6 +23,7 @@ // LOVE #include "common/int.h" #include "common/Color.h" +#include "common/StringMap.h" // C #include @@ -46,7 +47,7 @@ enum BuiltinVertexAttribute ATTRIB_MAX_ENUM }; -enum BuiltinVertexAttributeFlag +enum BuiltinVertexAttributeFlags { ATTRIBFLAG_POS = 1 << ATTRIB_POS, ATTRIBFLAG_TEXCOORD = 1 << ATTRIB_TEXCOORD, @@ -55,9 +56,9 @@ enum BuiltinVertexAttributeFlag enum BufferType { - BUFFER_VERTEX = 0, - BUFFER_INDEX, - BUFFER_MAX_ENUM + BUFFERTYPE_VERTEX = 0, + BUFFERTYPE_INDEX, + BUFFERTYPE_MAX_ENUM }; enum IndexDataType @@ -92,36 +93,81 @@ enum CullMode CULL_MAX_ENUM }; -namespace vertex +// The expected usage pattern of buffer data. +enum BufferUsage { - -// The expected usage pattern of vertex data. -enum Usage -{ - USAGE_STREAM, - USAGE_DYNAMIC, - USAGE_STATIC, - USAGE_MAX_ENUM + BUFFERUSAGE_STREAM, + BUFFERUSAGE_DYNAMIC, + BUFFERUSAGE_STATIC, + BUFFERUSAGE_MAX_ENUM }; -enum DataType +// Value types used when interfacing with the GPU (vertex and shader data). +// The order of this enum affects the dataFormatInfo array. +enum DataFormat { - DATA_SNORM8, - DATA_UNORM8, - DATA_INT8, - DATA_UINT8, + DATAFORMAT_FLOAT, + DATAFORMAT_FLOAT_VEC2, + DATAFORMAT_FLOAT_VEC3, + DATAFORMAT_FLOAT_VEC4, - DATA_SNORM16, - DATA_UNORM16, - DATA_INT16, - DATA_UINT16, + DATAFORMAT_FLOAT_MAT2X2, + DATAFORMAT_FLOAT_MAT2X3, + DATAFORMAT_FLOAT_MAT2X4, - DATA_INT32, - DATA_UINT32, + DATAFORMAT_FLOAT_MAT3X2, + DATAFORMAT_FLOAT_MAT3X3, + DATAFORMAT_FLOAT_MAT3X4, - DATA_FLOAT, + DATAFORMAT_FLOAT_MAT4X2, + DATAFORMAT_FLOAT_MAT4X3, + DATAFORMAT_FLOAT_MAT4X4, - DATA_MAX_ENUM + DATAFORMAT_INT32, + DATAFORMAT_INT32_VEC2, + DATAFORMAT_INT32_VEC3, + DATAFORMAT_INT32_VEC4, + + DATAFORMAT_UINT32, + DATAFORMAT_UINT32_VEC2, + DATAFORMAT_UINT32_VEC3, + DATAFORMAT_UINT32_VEC4, + + DATAFORMAT_SNORM8_VEC4, + DATAFORMAT_UNORM8_VEC4, + DATAFORMAT_INT8_VEC4, + DATAFORMAT_UINT8_VEC4, + + DATAFORMAT_SNORM16_VEC2, + DATAFORMAT_SNORM16_VEC4, + + DATAFORMAT_UNORM16_VEC2, + DATAFORMAT_UNORM16_VEC4, + + DATAFORMAT_INT16_VEC2, + DATAFORMAT_INT16_VEC4, + + DATAFORMAT_UINT16, + DATAFORMAT_UINT16_VEC2, + DATAFORMAT_UINT16_VEC4, + + DATAFORMAT_BOOL, + DATAFORMAT_BOOL_VEC2, + DATAFORMAT_BOOL_VEC3, + DATAFORMAT_BOOL_VEC4, + + DATAFORMAT_MAX_ENUM +}; + +enum DataBaseType +{ + DATA_BASETYPE_FLOAT, + DATA_BASETYPE_INT, + DATA_BASETYPE_UINT, + DATA_BASETYPE_SNORM, + DATA_BASETYPE_UNORM, + DATA_BASETYPE_BOOL, + DATA_BASETYPE_MAX_ENUM }; enum Winding @@ -131,12 +177,12 @@ enum Winding WINDING_MAX_ENUM }; -enum class TriangleIndexMode +enum TriangleIndexMode { - NONE, - STRIP, - FAN, - QUADS, + TRIANGLEINDEX_NONE, + TRIANGLEINDEX_STRIP, + TRIANGLEINDEX_FAN, + TRIANGLEINDEX_QUADS, }; enum class CommonFormat @@ -154,6 +200,17 @@ enum class CommonFormat XYf_STPf_RGBAub, }; +struct DataFormatInfo +{ + DataBaseType baseType; + bool isMatrix; + int components; + int matrixRows; + int matrixColumns; + size_t componentSize; + size_t size; +}; + struct STf_RGBAub { float s, t; @@ -185,6 +242,8 @@ struct XYf_STf_RGBAub Color32 color; }; +typedef XYf_STf_RGBAub Vertex; + struct XYf_STus_RGBAub { float x, y; @@ -221,42 +280,41 @@ struct BufferBindings void clear() { useBits = 0; } }; -struct AttributeInfo +struct VertexAttributeInfo { - DataType type; - uint8 components; uint8 bufferIndex; + DataFormat format : 8; uint16 offsetFromVertex; }; -struct BufferLayout +struct VertexBufferLayout { + // Attribute step rate is stored outside this struct as a bitmask. uint16 stride; }; -struct Attributes +struct VertexAttributes { static const uint32 MAX = 32; uint32 enableBits = 0; // indexed by attribute uint32 instanceBits = 0; // indexed by buffer - AttributeInfo attribs[MAX]; - BufferLayout bufferLayouts[BufferBindings::MAX]; + VertexAttributeInfo attribs[MAX]; + VertexBufferLayout bufferLayouts[BufferBindings::MAX]; - Attributes() {} - Attributes(CommonFormat format, uint8 bufferindex) + VertexAttributes() {} + VertexAttributes(CommonFormat format, uint8 bufferindex) { setCommonFormat(format, bufferindex); } - void set(uint32 index, DataType type, uint8 components, uint16 offsetfromvertex, uint8 bufferindex) + void set(uint32 index, DataFormat format, uint16 offsetfromvertex, uint8 bufferindex) { enableBits |= (1u << index); attribs[index].bufferIndex = bufferindex; - attribs[index].type = type; - attribs[index].components = components; + attribs[index].format = format; attribs[index].offsetFromVertex = offsetfromvertex; } @@ -306,51 +364,30 @@ inline CommonFormat getSinglePositionFormat(bool is2D) return is2D ? CommonFormat::XYf : CommonFormat::XYZf; } -size_t getIndexDataSize(IndexDataType type); -size_t getDataTypeSize(DataType datatype); -bool isDataTypeInteger(DataType datatype); +const DataFormatInfo &getDataFormatInfo(DataFormat format); +size_t getIndexDataSize(IndexDataType type); IndexDataType getIndexDataTypeFromMax(size_t maxvalue); +DataFormat getIndexDataFormat(IndexDataType type); +IndexDataType getIndexDataType(DataFormat format); int getIndexCount(TriangleIndexMode mode, int vertexCount); void fillIndices(TriangleIndexMode mode, uint16 vertexStart, uint16 vertexCount, uint16 *indices); void fillIndices(TriangleIndexMode mode, uint32 vertexStart, uint32 vertexCount, uint32 *indices); -bool getConstant(const char *in, BuiltinVertexAttribute &out); -bool getConstant(BuiltinVertexAttribute in, const char *&out); +STRINGMAP_DECLARE(BuiltinVertexAttribute); +STRINGMAP_DECLARE(BufferType); +STRINGMAP_DECLARE(IndexDataType); +STRINGMAP_DECLARE(BufferUsage); +STRINGMAP_DECLARE(PrimitiveType); +STRINGMAP_DECLARE(AttributeStep); +STRINGMAP_DECLARE(DataFormat); +STRINGMAP_DECLARE(DataBaseType); +STRINGMAP_DECLARE(CullMode); +STRINGMAP_DECLARE(Winding); -bool getConstant(const char *in, IndexDataType &out); -bool getConstant(IndexDataType in, const char *&out); -std::vector getConstants(IndexDataType); - -bool getConstant(const char *in, Usage &out); -bool getConstant(Usage in, const char *&out); -std::vector getConstants(Usage); - -bool getConstant(const char *in, PrimitiveType &out); -bool getConstant(PrimitiveType in, const char *&out); -std::vector getConstants(PrimitiveType); - -bool getConstant(const char *in, AttributeStep &out); -bool getConstant(AttributeStep in, const char *&out); -std::vector getConstants(AttributeStep); - -bool getConstant(const char *in, DataType &out); -bool getConstant(DataType in, const char *&out); -std::vector getConstants(DataType); - -bool getConstant(const char *in, CullMode &out); -bool getConstant(CullMode in, const char *&out); -std::vector getConstants(CullMode); - -bool getConstant(const char *in, Winding &out); -bool getConstant(Winding in, const char *&out); -std::vector getConstants(Winding); - -} // vertex - -typedef vertex::XYf_STf_RGBAub Vertex; +const char *getConstant(BuiltinVertexAttribute attrib); } // graphics } // love diff --git a/src/modules/graphics/wrap_Buffer.cpp b/src/modules/graphics/wrap_Buffer.cpp new file mode 100644 index 000000000..78813170b --- /dev/null +++ b/src/modules/graphics/wrap_Buffer.cpp @@ -0,0 +1,474 @@ +/** +* Copyright (c) 2006-2020 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. +**/ + +#include "wrap_Buffer.h" +#include "Buffer.h" +#include "common/Data.h" + +namespace love +{ +namespace graphics +{ + +static const double defaultComponents[] = {0.0, 0.0, 0.0, 1.0}; + +template +static inline size_t writeData(lua_State *L, int startidx, int components, char *data) +{ + auto componentdata = (T *) data; + + for (int i = 0; i < components; i++) + componentdata[i] = (T) (luaL_optnumber(L, startidx + i, defaultComponents[i])); + + return sizeof(T) * components; +} + +template +static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data) +{ + auto componentdata = (T *) data; + const auto maxval = std::numeric_limits::max(); + + for (int i = 0; i < components; i++) + componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval); + + return sizeof(T) * components; +} + +template +static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data) +{ + auto componentdata = (T *) data; + const auto maxval = std::numeric_limits::max(); + + for (int i = 0; i < components; i++) + componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval); + + return sizeof(T) * components; +} + +void luax_writebufferdata(lua_State *L, int startidx, DataFormat format, char *data) +{ + switch (format) + { + case DATAFORMAT_FLOAT: writeData(L, startidx, 1, data); break; + case DATAFORMAT_FLOAT_VEC2: writeData(L, startidx, 2, data); break; + case DATAFORMAT_FLOAT_VEC3: writeData(L, startidx, 3, data); break; + case DATAFORMAT_FLOAT_VEC4: writeData(L, startidx, 4, data); break; + + case DATAFORMAT_INT32: writeData(L, startidx, 1, data); break; + case DATAFORMAT_INT32_VEC2: writeData(L, startidx, 2, data); break; + case DATAFORMAT_INT32_VEC3: writeData(L, startidx, 3, data); break; + case DATAFORMAT_INT32_VEC4: writeData(L, startidx, 4, data); break; + + case DATAFORMAT_UINT32: writeData(L, startidx, 1, data); break; + case DATAFORMAT_UINT32_VEC2: writeData(L, startidx, 2, data); break; + case DATAFORMAT_UINT32_VEC3: writeData(L, startidx, 3, data); break; + case DATAFORMAT_UINT32_VEC4: writeData(L, startidx, 4, data); break; + + case DATAFORMAT_SNORM8_VEC4: writeSNormData(L, startidx, 4, data); break; + case DATAFORMAT_UNORM8_VEC4: writeUNormData(L, startidx, 4, data); break; + case DATAFORMAT_INT8_VEC4: writeData(L, startidx, 4, data); break; + case DATAFORMAT_UINT8_VEC4: writeData(L, startidx, 4, data); break; + + case DATAFORMAT_SNORM16_VEC2: writeSNormData(L, startidx, 2, data); break; + case DATAFORMAT_SNORM16_VEC4: writeSNormData(L, startidx, 4, data); break; + + case DATAFORMAT_UNORM16_VEC2: writeUNormData(L, startidx, 2, data); break; + case DATAFORMAT_UNORM16_VEC4: writeUNormData(L, startidx, 4, data); break; + + case DATAFORMAT_INT16_VEC2: writeData(L, startidx, 2, data); break; + case DATAFORMAT_INT16_VEC4: writeData(L, startidx, 4, data); break; + + case DATAFORMAT_UINT16: writeData(L, startidx, 1, data); break; + case DATAFORMAT_UINT16_VEC2: writeData(L, startidx, 2, data); break; + case DATAFORMAT_UINT16_VEC4: writeData(L, startidx, 4, data); break; + + default: break; + } +} + +template +static inline size_t readData(lua_State *L, int components, const char *data) +{ + const auto componentdata = (const T *) data; + + for (int i = 0; i < components; i++) + lua_pushnumber(L, (lua_Number) componentdata[i]); + + return sizeof(T) * components; +} + +template +static inline size_t readSNormData(lua_State *L, int components, const char *data) +{ + const auto componentdata = (const T *) data; + const auto maxval = std::numeric_limits::max(); + + for (int i = 0; i < components; i++) + lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval)); + + return sizeof(T) * components; +} + +template +static inline size_t readUNormData(lua_State *L, int components, const char *data) +{ + const auto componentdata = (const T *) data; + const auto maxval = std::numeric_limits::max(); + + for (int i = 0; i < components; i++) + lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval); + + return sizeof(T) * components; +} + +void luax_readbufferdata(lua_State *L, DataFormat format, const char *data) +{ + switch (format) + { + case DATAFORMAT_FLOAT: readData(L, 1, data); break; + case DATAFORMAT_FLOAT_VEC2: readData(L, 2, data); break; + case DATAFORMAT_FLOAT_VEC3: readData(L, 3, data); break; + case DATAFORMAT_FLOAT_VEC4: readData(L, 4, data); break; + + case DATAFORMAT_INT32: readData(L, 1, data); break; + case DATAFORMAT_INT32_VEC2: readData(L, 2, data); break; + case DATAFORMAT_INT32_VEC3: readData(L, 3, data); break; + case DATAFORMAT_INT32_VEC4: readData(L, 4, data); break; + + case DATAFORMAT_UINT32: readData(L, 1, data); break; + case DATAFORMAT_UINT32_VEC2: readData(L, 2, data); break; + case DATAFORMAT_UINT32_VEC3: readData(L, 3, data); break; + case DATAFORMAT_UINT32_VEC4: readData(L, 4, data); break; + + case DATAFORMAT_SNORM8_VEC4: readSNormData(L, 4, data); break; + case DATAFORMAT_UNORM8_VEC4: readUNormData(L, 4, data); break; + case DATAFORMAT_INT8_VEC4: readData(L, 4, data); break; + case DATAFORMAT_UINT8_VEC4: readData(L, 4, data); break; + + case DATAFORMAT_SNORM16_VEC2: readSNormData(L, 2, data); break; + case DATAFORMAT_SNORM16_VEC4: readSNormData(L, 4, data); break; + + case DATAFORMAT_UNORM16_VEC2: readUNormData(L, 2, data); break; + case DATAFORMAT_UNORM16_VEC4: readUNormData(L, 4, data); break; + + case DATAFORMAT_INT16_VEC2: readData(L, 2, data); break; + case DATAFORMAT_INT16_VEC4: readData(L, 4, data); break; + + case DATAFORMAT_UINT16: readData(L, 1, data); break; + case DATAFORMAT_UINT16_VEC2: readData(L, 2, data); break; + case DATAFORMAT_UINT16_VEC4: readData(L, 4, data); break; + + default: break; + } +} + +Buffer *luax_checkbuffer(lua_State *L, int idx) +{ + return luax_checktype(L, idx); +} + +static int w_Buffer_flush(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + t->unmap(); + return 0; +} + +static int w_Buffer_setArrayData(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + + int startindex = (int) luaL_optnumber(L, 3, 1) - 1; + + int count = -1; + if (!lua_isnoneornil(L, 4)) + { + count = (int) luaL_checknumber(L, 4); + if (count <= 0) + return luaL_error(L, "Element count must be greater than 0."); + } + + size_t stride = t->getArrayStride(); + size_t offset = startindex * stride; + int arraylength = (int) t->getArrayLength(); + + if (startindex >= arraylength || startindex < 0) + return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", arraylength); + + if (luax_istype(L, 2, Data::type)) + { + Data *d = luax_checktype(L, 2); + + count = count >= 0 ? count : (arraylength - startindex); + if (startindex + count > arraylength) + return luaL_error(L, "Too many array elements (expected at most %d, got %d)", arraylength - startindex, count); + + size_t datasize = std::min(d->getSize(), count * stride); + char *bytedata = (char *) t->map() + offset; + + memcpy(bytedata, d->getData(), datasize); + + t->setMappedRangeModified(offset, datasize); + t->unmap(); + return 0; + } + + const std::vector &members = t->getDataMembers(); + + int ncomponents = 0; + for (const Buffer::DataMember &member : members) + ncomponents += member.info.components; + + luaL_checktype(L, 2, LUA_TTABLE); + int tablelen = (int) luax_objlen(L, 2); + + lua_rawgeti(L, 2, 1); + bool tableoftables = lua_istable(L, -1); + lua_pop(L, 1); + + if (!tableoftables) + { + if (tablelen % ncomponents != 0) + return luaL_error(L, "Array length in flat array variant of Buffer:setArrayData must be a multiple of the total number of components (%d)", ncomponents); + tablelen /= ncomponents; + } + + count = count >= 0 ? std::min(count, tablelen) : tablelen; + if (startindex + count > arraylength) + return luaL_error(L, "Too many array elements (expected at most %d, got %d)", arraylength - startindex, count); + + char *data = (char *) t->map() + offset; + + if (tableoftables) + { + for (int i = 0; i < count; i++) + { + // get arraydata[index] + lua_rawgeti(L, 2, i + 1); + luaL_checktype(L, -1, LUA_TTABLE); + + // get arraydata[index][j] + for (int j = 1; j <= ncomponents; j++) + lua_rawgeti(L, -j, j); + + int idx = -ncomponents; + + for (const Buffer::DataMember &member : members) + { + luax_writebufferdata(L, idx, member.decl.format, data + member.offset); + idx += member.info.components; + } + + lua_pop(L, ncomponents + 1); + data += stride; + } + } + else // Flat array + { + for (int i = 0; i < count; i++) + { + // get arraydata[arrayindex * ncomponents + componentindex] + for (int componentindex = 1; componentindex <= ncomponents; componentindex++) + lua_rawgeti(L, 2, i * ncomponents + componentindex); + + int idx = -ncomponents; + + for (const Buffer::DataMember &member : members) + { + luax_writebufferdata(L, idx, member.decl.format, data + member.offset); + idx += member.info.components; + } + + lua_pop(L, ncomponents); + data += stride; + } + } + + t->setMappedRangeModified(offset, count * stride); + t->unmap(); + + return 0; +} + +static int w_Buffer_setElement(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + + size_t index = (size_t) (luaL_checkinteger(L, 2) - 1); + if (index >= t->getArrayLength()) + return luaL_error(L, "Invalid Buffer element index: %ld", index); + + size_t stride = t->getArrayStride(); + size_t offset = index * stride; + char *data = (char *) t->map() + offset; + const auto &members = t->getDataMembers(); + + bool istable = lua_istable(L, 3); + int idx = istable ? 1 : 3; + + if (istable) + { + for (const Buffer::DataMember &member : members) + { + int components = member.info.components; + + for (int i = idx; i < idx + components; i++) + lua_rawgeti(L, 3, i); + + luax_writebufferdata(L, -components, member.decl.format, data + member.offset); + + idx += components; + lua_pop(L, components); + } + } + else + { + for (const Buffer::DataMember &member : members) + { + luax_writebufferdata(L, idx, member.decl.format, data + member.offset); + idx += member.info.components; + } + } + + t->setMappedRangeModified(offset, stride); + return 0; +} + +static int w_Buffer_getElement(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + if ((t->getMapFlags() & Buffer::MAP_READ) == 0) + return luaL_error(L, "Buffer:getElement requires the buffer to be created with the 'cpureadable' setting set to true."); + + size_t index = (size_t) (luaL_checkinteger(L, 2) - 1); + if (index >= t->getArrayLength()) + return luaL_error(L, "Invalid Buffer element index: %ld", index); + + size_t offset = index * t->getArrayStride(); + const char *data = (const char *) t->map() + offset; + const auto &members = t->getDataMembers(); + + int n = 0; + + for (const Buffer::DataMember &member : members) + { + luax_readbufferdata(L, member.decl.format, data + member.offset); + n += member.info.components; + } + + return n; +} + +static int w_Buffer_getElementCount(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + lua_pushinteger(L, t->getArrayLength()); + return 1; +} + +static int w_Buffer_getElementStride(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + lua_pushinteger(L, t->getArrayStride()); + return 1; +} + +static int w_Buffer_getSize(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + lua_pushinteger(L, t->getSize()); + return 1; +} + +static int w_Buffer_getFormat(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + const auto &members = t->getDataMembers(); + + lua_createtable(L, (int) members.size(), 0); + + for (size_t i = 0; i < members.size(); i++) + { + const Buffer::DataMember &member = members[i]; + + lua_createtable(L, 0, 4); + + lua_pushstring(L, member.decl.name.c_str()); + lua_setfield(L, -2, "name"); + + const char *formatstr = "unknown"; + getConstant(member.decl.format, formatstr); + lua_pushstring(L, formatstr); + lua_setfield(L, -2, "format"); + + lua_pushinteger(L, member.decl.arrayLength); + lua_setfield(L, -2, "arraylength"); + + lua_pushinteger(L, member.offset); + lua_setfield(L, -2, "offset"); + + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +static int w_Buffer_isBufferType(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + BufferType buffertype = BUFFERTYPE_MAX_ENUM; + const char *typestr = luaL_checkstring(L, 2); + if (!getConstant(typestr, buffertype)) + return luax_enumerror(L, "buffer type", getConstants(buffertype), typestr); + luax_pushboolean(L, (t->getTypeFlags() & (1 << buffertype)) != 0); + return 1; +} + +static int w_Buffer_isCPUReadable(lua_State *L) +{ + Buffer *t = luax_checkbuffer(L, 1); + luax_pushboolean(L, (t->getMapFlags() & Buffer::MAP_READ) != 0); + return 1; +} + +static const luaL_Reg w_Buffer_functions[] = +{ + { "flush", w_Buffer_flush }, + { "setArrayData", w_Buffer_setArrayData }, + { "setElement", w_Buffer_setElement }, + { "getElement", w_Buffer_getElement }, + { "getElementCount", w_Buffer_getElementCount }, + { "getElementStride", w_Buffer_getElementStride }, + { "getSize", w_Buffer_getSize }, + { "getFormat", w_Buffer_getFormat }, + { "isBufferType", w_Buffer_isBufferType }, + { "isCPUReadable", w_Buffer_isCPUReadable }, + { 0, 0 } +}; + +extern "C" int luaopen_graphicsbuffer(lua_State *L) +{ + return luax_register_type(L, &Buffer::type, w_Buffer_functions, nullptr); +} + +} // graphics +} // love diff --git a/src/modules/graphics/wrap_Buffer.h b/src/modules/graphics/wrap_Buffer.h new file mode 100644 index 000000000..3fdfeb6ca --- /dev/null +++ b/src/modules/graphics/wrap_Buffer.h @@ -0,0 +1,39 @@ +/** +* Copyright (c) 2006-2020 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 + +// LOVE +#include "common/runtime.h" +#include "Buffer.h" + +namespace love +{ +namespace graphics +{ + +void luax_writebufferdata(lua_State *L, int startidx, DataFormat format, char *data); +void luax_readbufferdata(lua_State *L, DataFormat format, const char *data); + +Buffer *luax_checkbuffer(lua_State *L, int idx); +extern "C" int luaopen_graphicsbuffer(lua_State *L); + +} // graphics +} // love diff --git a/src/modules/graphics/wrap_Graphics.cpp b/src/modules/graphics/wrap_Graphics.cpp index 8e8629e15..a479411b3 100644 --- a/src/modules/graphics/wrap_Graphics.cpp +++ b/src/modules/graphics/wrap_Graphics.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -1324,12 +1325,12 @@ int w_newSpriteBatch(lua_State *L) Texture *texture = luax_checktexture(L, 1); int size = (int) luaL_optinteger(L, 2, 1000); - vertex::Usage usage = vertex::USAGE_DYNAMIC; + BufferUsage usage = BUFFERUSAGE_DYNAMIC; if (lua_gettop(L) > 2) { const char *usagestr = luaL_checkstring(L, 3); - if (!vertex::getConstant(usagestr, usage)) - return luax_enumerror(L, "usage hint", vertex::getConstants(usage), usagestr); + if (!getConstant(usagestr, usage)) + return luax_enumerror(L, "usage hint", getConstants(usage), usagestr); } SpriteBatch *t = nullptr; @@ -1487,22 +1488,309 @@ int w_validateShader(lua_State *L) return 1; } -static vertex::Usage luax_optmeshusage(lua_State *L, int idx, vertex::Usage def) +static BufferUsage luax_optbufferusage(lua_State *L, int idx, BufferUsage def) { const char *usagestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx); - if (usagestr && !vertex::getConstant(usagestr, def)) - luax_enumerror(L, "usage hint", vertex::getConstants(def), usagestr); + if (usagestr && !getConstant(usagestr, def)) + luax_enumerror(L, "usage hint", getConstants(def), usagestr); return def; } +static void luax_optbuffersettings(lua_State *L, int idx, Buffer::Settings &settings) +{ + if (lua_isnoneornil(L, idx)) + return; + + luaL_checktype(L, idx, LUA_TTABLE); + + lua_getfield(L, idx, "usage"); + settings.usage = luax_optbufferusage(L, -1, settings.usage); + lua_pop(L, 1); + + if (luax_boolflag(L, idx, "cpureadable", settings.mapFlags & Buffer::MAP_READ)) + settings.mapFlags = (Buffer::MapFlags)(settings.mapFlags | Buffer::MAP_READ); + else + settings.mapFlags = (Buffer::MapFlags)(settings.mapFlags & (~Buffer::MAP_READ)); +} + +static void luax_checkbufferformat(lua_State *L, int idx, std::vector &format) +{ + luaL_checktype(L, idx, LUA_TTABLE); + int tablelen = luax_objlen(L, idx); + + for (int i = 1; i <= tablelen; i++) + { + lua_rawgeti(L, idx, i); + luaL_checktype(L, -1, LUA_TTABLE); + + Buffer::DataDeclaration decl("", DATAFORMAT_MAX_ENUM); + + lua_getfield(L, -1, "name"); + if (lua_type(L, -1) != LUA_TSTRING) + { + std::ostringstream ss; + ss << "'name' field expected in array element #"; + ss << i; + ss << " of format table"; + std::string str = ss.str(); + luaL_argerror(L, idx, str.c_str()); + } + decl.name = luax_checkstring(L, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "format"); + if (lua_type(L, -1) != LUA_TSTRING) + { + std::ostringstream ss; + ss << "'format' field expected in array element #"; + ss << i; + ss << " of format table"; + std::string str = ss.str(); + luaL_argerror(L, idx, str.c_str()); + } + const char *formatstr = luaL_checkstring(L, -1); + if (!getConstant(formatstr, decl.format)) + luax_enumerror(L, "data format", getConstants(decl.format), formatstr); + lua_pop(L, 1); + + decl.arrayLength = luax_intflag(L, -1, "arraylength", 0); + + format.push_back(decl); + lua_pop(L, 1); + } +} + +static Buffer *luax_newbuffer(lua_State *L, int idx, const Buffer::Settings &settings, const std::vector &format) +{ + size_t arraylength = 0; + size_t bytesize = 0; + Data *data = nullptr; + const void *initialdata = nullptr; + + int ncomponents = 0; + for (const Buffer::DataDeclaration &decl : format) + ncomponents += getDataFormatInfo(decl.format).components; + + if (luax_istype(L, idx, Data::type)) + { + data = luax_checktype(L, idx); + initialdata = data->getData(); + bytesize = data->getSize(); + } + + bool tableoftables = false; + + if (lua_istable(L, idx)) + { + arraylength = luax_objlen(L, idx); + + lua_rawgeti(L, idx, 1); + tableoftables = lua_istable(L, -1); + lua_pop(L, 1); + + if (!tableoftables) + { + if (arraylength % ncomponents != 0) + luaL_error(L, "Array length in flat array variant of newBuffer must be a multiple of the total number of components (%d)", ncomponents); + arraylength /= ncomponents; + } + } + else if (data == nullptr) + { + lua_Integer len = luaL_checkinteger(L, idx); + if (len <= 0) + luaL_argerror(L, idx, "number of elements must be greater than 0"); + arraylength = (size_t) len; + } + + Buffer *b = nullptr; + luax_catchexcept(L, [&] { b = instance()->newBuffer(settings, format, initialdata, bytesize, arraylength); }); + + if (lua_istable(L, idx)) + { + Buffer::Mapper mapper(*b); + char *data = (char *) mapper.data; + const auto &members = b->getDataMembers(); + size_t stride = b->getArrayStride(); + + if (tableoftables) + { + for (size_t i = 0; i < arraylength; i++) + { + // get arraydata[index] + lua_rawgeti(L, 2, i + 1); + luaL_checktype(L, -1, LUA_TTABLE); + + // get arraydata[index][j] + for (int j = 1; j <= ncomponents; j++) + lua_rawgeti(L, -j, j); + + int idx = -ncomponents; + + for (const Buffer::DataMember &member : members) + { + luax_writebufferdata(L, idx, member.decl.format, data + member.offset); + idx += member.info.components; + } + + lua_pop(L, ncomponents + 1); + data += stride; + } + } + else // Flat array + { + for (size_t i = 0; i < arraylength; i++) + { + // get arraydata[arrayindex * ncomponents + componentindex] + for (int componentindex = 1; componentindex <= ncomponents; componentindex++) + lua_rawgeti(L, 2, i * ncomponents + componentindex); + + int idx = -ncomponents; + + for (const Buffer::DataMember &member : members) + { + luax_writebufferdata(L, idx, member.decl.format, data + member.offset); + idx += member.info.components; + } + + lua_pop(L, ncomponents); + data += stride; + } + } + } + + return b; +} + +int w_newBuffer(lua_State *L) +{ + Buffer::Settings settings(0, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC); + + luaL_checktype(L, 3, LUA_TTABLE); + + for (int i = 0; i < BUFFERTYPE_MAX_ENUM; i++) + { + BufferType buffertype = (BufferType) i; + const char *tname = nullptr; + if (!getConstant(buffertype, tname)) + continue; + if (luax_boolflag(L, 3, tname, false)) + settings.typeFlags = (Buffer::TypeFlags)(settings.typeFlags | (1u << i)); + } + + luax_optbuffersettings(L, 3, settings); + + std::vector format; + luax_checkbufferformat(L, 1, format); + + Buffer *b = luax_newbuffer(L, 2, settings, format); + + luax_pushtype(L, b); + b->release(); + return 1; +} + +int w_newVertexBuffer(lua_State *L) +{ + Buffer::Settings settings(Buffer::TYPEFLAG_VERTEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC); + luax_optbuffersettings(L, 3, settings); + + std::vector format; + luax_checkbufferformat(L, 1, format); + + Buffer *b = luax_newbuffer(L, 2, settings, format); + + luax_pushtype(L, b); + b->release(); + return 1; +} + +int w_newIndexBuffer(lua_State *L) +{ + Buffer::Settings settings(Buffer::TYPEFLAG_INDEX, Buffer::MAP_EXPLICIT_RANGE_MODIFY, BUFFERUSAGE_DYNAMIC); + luax_optbuffersettings(L, 3, settings); + + size_t arraylength = 0; + size_t bytesize = 0; + DataFormat format = DATAFORMAT_UINT16; + Data *data = nullptr; + const void *initialdata = nullptr; + + if (luax_istype(L, 1, Data::type)) + { + data = luax_checktype(L, 1); + initialdata = data->getData(); + bytesize = data->getSize(); + } + + if (lua_istable(L, 1)) + { + arraylength = luax_objlen(L, 1); + + // Scan array for invalid types and the max value. + lua_Integer maxvalue = 0; + for (size_t i = 0; i < arraylength; i++) + { + lua_rawgeti(L, 1, i + 1); + lua_Integer v = luaL_checkinteger(L, -1) - 1; + lua_pop(L, 1); + if (v < 0) + return luaL_argerror(L, 1, "expected positive integer values in array"); + else + maxvalue = std::max(maxvalue, v); + } + + format = getIndexDataFormat(getIndexDataTypeFromMax(maxvalue)); + } + else if (data == nullptr) + { + lua_Integer len = luaL_checkinteger(L, 1); + if (len <= 0) + return luaL_argerror(L, 1, "number of elements must be greater than 0"); + arraylength = (size_t) len; + } + + if (data != nullptr || !lua_isnoneornil(L, 2)) + { + const char *formatstr = luaL_checkstring(L, 2); + if (!getConstant(formatstr, format)) + return luax_enumerror(L, "index data format", getConstants(format), formatstr); + } + + Buffer *b = nullptr; + luax_catchexcept(L, [&] { b = instance()->newBuffer(settings, format, initialdata, bytesize, arraylength); }); + + if (lua_istable(L, 1)) + { + Buffer::Mapper mapper(*b); + uint16 *u16data = (uint16 *) mapper.data; + uint32 *u32data = (uint32 *) mapper.data; + + for (size_t i = 0; i < arraylength; i++) + { + lua_rawgeti(L, 1, i + 1); + lua_Integer v = luaL_checkinteger(L, -1) - 1; + lua_pop(L, 1); + if (format == DATAFORMAT_UINT16) + u16data[i] = (uint16) v; + else + u32data[i] = (uint32) v; + } + } + + luax_pushtype(L, b); + b->release(); + return 1; +} + static PrimitiveType luax_optmeshdrawmode(lua_State *L, int idx, PrimitiveType def) { const char *modestr = lua_isnoneornil(L, idx) ? nullptr : luaL_checkstring(L, idx); - if (modestr && !vertex::getConstant(modestr, def)) - luax_enumerror(L, "mesh draw mode", vertex::getConstants(def), modestr); + if (modestr && !getConstant(modestr, def)) + luax_enumerror(L, "mesh draw mode", getConstants(def), modestr); return def; } @@ -1512,7 +1800,9 @@ static Mesh *newStandardMesh(lua_State *L) Mesh *t = nullptr; PrimitiveType drawmode = luax_optmeshdrawmode(L, 2, PRIMITIVE_TRIANGLE_FAN); - vertex::Usage usage = luax_optmeshusage(L, 3, vertex::USAGE_DYNAMIC); + BufferUsage usage = luax_optbufferusage(L, 3, BUFFERUSAGE_DYNAMIC); + + std::vector format = Mesh::getDefaultVertexFormat(); // First argument is a table of standard vertices, or the number of // standard vertices. @@ -1552,12 +1842,12 @@ static Mesh *newStandardMesh(lua_State *L) vertices.push_back(v); } - luax_catchexcept(L, [&](){ t = instance()->newMesh(vertices, drawmode, usage); }); + luax_catchexcept(L, [&](){ t = instance()->newMesh(format, vertices.data(), vertices.size() * sizeof(Vertex), drawmode, usage); }); } else { int count = (int) luaL_checkinteger(L, 1); - luax_catchexcept(L, [&](){ t = instance()->newMesh(count, drawmode, usage); }); + luax_catchexcept(L, [&](){ t = instance()->newMesh(format, count, drawmode, usage); }); } return t; @@ -1569,10 +1859,10 @@ static Mesh *newCustomMesh(lua_State *L) // First argument is the vertex format, second is a table of vertices or // the number of vertices. - std::vector vertexformat; + std::vector vertexformat; PrimitiveType drawmode = luax_optmeshdrawmode(L, 3, PRIMITIVE_TRIANGLE_FAN); - vertex::Usage usage = luax_optmeshusage(L, 4, vertex::USAGE_DYNAMIC); + BufferUsage usage = luax_optbufferusage(L, 4, BUFFERUSAGE_DYNAMIC); lua_rawgeti(L, 1, 1); if (!lua_istable(L, -1)) @@ -1591,27 +1881,53 @@ static Mesh *newCustomMesh(lua_State *L) for (int j = 1; j <= 3; j++) lua_rawgeti(L, -j, j); - Mesh::AttribFormat format; - format.name = luaL_checkstring(L, -3); + const char *name = luaL_checkstring(L, -3); + DataFormat format = DATAFORMAT_MAX_ENUM; const char *tname = luaL_checkstring(L, -2); - if (strcmp(tname, "byte") == 0) // Legacy name. - format.type = vertex::DATA_UNORM8; - else if (!vertex::getConstant(tname, format.type)) + + if (!lua_isnoneornil(L, -1)) { - luax_enumerror(L, "Mesh vertex data type name", vertex::getConstants(format.type), tname); - return nullptr; + int components = (int) luaL_checkinteger(L, -1); + + // Check deprecated format names. + if (strcmp(tname, "byte") == 0 || strcmp(tname, "unorm8") == 0) + { + if (components == 4) + format = DATAFORMAT_UNORM8_VEC4; + else + luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname); + } + else if (strcmp(tname, "unorm16") == 0) + { + if (components == 2) + format = DATAFORMAT_UNORM16_VEC2; + else if (components == 4) + format = DATAFORMAT_UNORM16_VEC4; + else + luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname); + + } + else if (strcmp(tname, "float") == 0) + { + if (components == 1) + format = DATAFORMAT_FLOAT; + else if (components == 2) + format = DATAFORMAT_FLOAT_VEC2; + else if (components == 3) + format = DATAFORMAT_FLOAT_VEC3; + else if (components == 4) + format = DATAFORMAT_FLOAT_VEC4; + else + luaL_error(L, "Invalid component count (%d) for vertex data type %s", components, tname); + } } - format.components = (int) luaL_checkinteger(L, -1); - if (format.components <= 0 || format.components > 4) - { - luaL_error(L, "Number of vertex attribute components must be between 1 and 4 (got %d)", format.components); - return nullptr; - } + if (format == DATAFORMAT_MAX_ENUM && !getConstant(tname, format)) + luax_enumerror(L, "vertex data format", getConstants(format), tname); lua_pop(L, 4); - vertexformat.push_back(format); + vertexformat.emplace_back(name, format); } if (lua_isnumber(L, 2)) @@ -1636,10 +1952,6 @@ static Mesh *newCustomMesh(lua_State *L) } lua_pop(L, 1); - int vertexcomponents = 0; - for (const Mesh::AttribFormat &format : vertexformat) - vertexcomponents += format.components; - size_t numvertices = luax_objlen(L, 2); luax_catchexcept(L, [&](){ t = instance()->newMesh(vertexformat, numvertices, drawmode, usage); }); @@ -1656,19 +1968,19 @@ static Mesh *newCustomMesh(lua_State *L) int n = 0; for (size_t i = 0; i < vertexformat.size(); i++) { - int components = vertexformat[i].components; + const auto &info = getDataFormatInfo(vertexformat[i].format); // get vertices[vertindex][n] - for (int c = 0; c < components; c++) + for (int c = 0; c < info.components; c++) { n++; lua_rawgeti(L, -(c + 1), n); } // Fetch the values from Lua and store them in data buffer. - luax_writeAttributeData(L, -components, vertexformat[i].type, components, data); + luax_writebufferdata(L, -info.components, vertexformat[i].format, data); - lua_pop(L, components); + lua_pop(L, info.components); luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, i, data, sizeof(float) * 4); }, @@ -2171,8 +2483,8 @@ int w_setMeshCullMode(lua_State *L) const char *str = luaL_checkstring(L, 1); CullMode mode; - if (!vertex::getConstant(str, mode)) - return luax_enumerror(L, "cull mode", vertex::getConstants(mode), str); + if (!getConstant(str, mode)) + return luax_enumerror(L, "cull mode", getConstants(mode), str); luax_catchexcept(L, [&]() { instance()->setMeshCullMode(mode); }); return 0; @@ -2182,7 +2494,7 @@ int w_getMeshCullMode(lua_State *L) { CullMode mode = instance()->getMeshCullMode(); const char *str; - if (!vertex::getConstant(mode, str)) + if (!getConstant(mode, str)) return luaL_error(L, "Unknown cull mode"); lua_pushstring(L, str); return 1; @@ -2191,10 +2503,10 @@ int w_getMeshCullMode(lua_State *L) int w_setFrontFaceWinding(lua_State *L) { const char *str = luaL_checkstring(L, 1); - vertex::Winding winding; + Winding winding; - if (!vertex::getConstant(str, winding)) - return luax_enumerror(L, "vertex winding", vertex::getConstants(winding), str); + if (!getConstant(str, winding)) + return luax_enumerror(L, "vertex winding", getConstants(winding), str); luax_catchexcept(L, [&]() { instance()->setFrontFaceWinding(winding); }); return 0; @@ -2202,9 +2514,9 @@ int w_setFrontFaceWinding(lua_State *L) int w_getFrontFaceWinding(lua_State *L) { - vertex::Winding winding = instance()->getFrontFaceWinding(); + Winding winding = instance()->getFrontFaceWinding(); const char *str; - if (!vertex::getConstant(winding, str)) + if (!getConstant(winding, str)) return luaL_error(L, "Unknown vertex winding"); lua_pushstring(L, str); return 1; @@ -3073,6 +3385,9 @@ static const luaL_Reg functions[] = { "newSpriteBatch", w_newSpriteBatch }, { "newParticleSystem", w_newParticleSystem }, { "newShader", w_newShader }, + { "newBuffer", w_newBuffer }, + { "newVertexBuffer", w_newVertexBuffer }, + { "newIndexBuffer", w_newIndexBuffer }, { "newMesh", w_newMesh }, { "newText", w_newText }, { "_newVideo", w_newVideo }, @@ -3202,6 +3517,7 @@ static const lua_CFunction types[] = luaopen_texture, luaopen_font, luaopen_quad, + luaopen_graphicsbuffer, luaopen_spritebatch, luaopen_particlesystem, luaopen_shader, diff --git a/src/modules/graphics/wrap_Graphics.h b/src/modules/graphics/wrap_Graphics.h index afd4240ec..24fac737c 100644 --- a/src/modules/graphics/wrap_Graphics.h +++ b/src/modules/graphics/wrap_Graphics.h @@ -31,6 +31,7 @@ #include "wrap_Mesh.h" #include "wrap_Text.h" #include "wrap_Video.h" +#include "wrap_Buffer.h" #include "Graphics.h" namespace love diff --git a/src/modules/graphics/wrap_Mesh.cpp b/src/modules/graphics/wrap_Mesh.cpp index 3c9de2f5f..211463033 100644 --- a/src/modules/graphics/wrap_Mesh.cpp +++ b/src/modules/graphics/wrap_Mesh.cpp @@ -20,6 +20,7 @@ // LOVE #include "wrap_Mesh.h" +#include "wrap_Buffer.h" #include "Texture.h" #include "wrap_Texture.h" @@ -36,140 +37,6 @@ Mesh *luax_checkmesh(lua_State *L, int idx) return luax_checktype(L, idx); } -static const double defaultComponents[] = {0.0, 0.0, 0.0, 1.0}; - -template -static inline size_t writeData(lua_State *L, int startidx, int components, char *data) -{ - auto componentdata = (T *) data; - - for (int i = 0; i < components; i++) - componentdata[i] = (T) (luaL_optnumber(L, startidx + i, defaultComponents[i])); - - return sizeof(T) * components; -} - -template -static inline size_t writeSNormData(lua_State *L, int startidx, int components, char *data) -{ - auto componentdata = (T *) data; - auto maxval = std::numeric_limits::max(); - - for (int i = 0; i < components; i++) - componentdata[i] = (T) (luax_optnumberclamped(L, startidx + i, -1.0, 1.0, defaultComponents[i]) * maxval); - - return sizeof(T) * components; -} - -template -static inline size_t writeUNormData(lua_State *L, int startidx, int components, char *data) -{ - auto componentdata = (T *) data; - auto maxval = std::numeric_limits::max(); - - for (int i = 0; i < components; i++) - componentdata[i] = (T) (luax_optnumberclamped01(L, startidx + i, 1.0) * maxval); - - return sizeof(T) * components; -} - -char *luax_writeAttributeData(lua_State *L, int startidx, vertex::DataType type, int components, char *data) -{ - switch (type) - { - case vertex::DATA_SNORM8: - return data + writeSNormData(L, startidx, components, data); - case vertex::DATA_UNORM8: - return data + writeUNormData(L, startidx, components, data); - case vertex::DATA_INT8: - return data + writeData(L, startidx, components, data); - case vertex::DATA_UINT8: - return data + writeData(L, startidx, components, data); - case vertex::DATA_SNORM16: - return data + writeSNormData(L, startidx, components, data); - case vertex::DATA_UNORM16: - return data + writeUNormData(L, startidx, components, data); - case vertex::DATA_INT16: - return data + writeData(L, startidx, components, data); - case vertex::DATA_UINT16: - return data + writeData(L, startidx, components, data); - case vertex::DATA_INT32: - return data + writeData(L, startidx, components, data); - case vertex::DATA_UINT32: - return data + writeData(L, startidx, components, data); - case vertex::DATA_FLOAT: - return data + writeData(L, startidx, components, data); - default: - return data; - } -} - -template -static inline size_t readData(lua_State *L, int components, const char *data) -{ - auto componentdata = (const T *) data; - - for (int i = 0; i < components; i++) - lua_pushnumber(L, (lua_Number) componentdata[i]); - - return sizeof(T) * components; -} - -template -static inline size_t readSNormData(lua_State *L, int components, const char *data) -{ - auto componentdata = (const T *) data; - auto maxval = std::numeric_limits::max(); - - for (int i = 0; i < components; i++) - lua_pushnumber(L, std::max(-1.0, (lua_Number) componentdata[i] / (lua_Number)maxval)); - - return sizeof(T) * components; -} - -template -static inline size_t readUNormData(lua_State *L, int components, const char *data) -{ - auto componentdata = (const T *) data; - auto maxval = std::numeric_limits::max(); - - for (int i = 0; i < components; i++) - lua_pushnumber(L, (lua_Number) componentdata[i] / (lua_Number)maxval); - - return sizeof(T) * components; -} - -const char *luax_readAttributeData(lua_State *L, vertex::DataType type, int components, const char *data) -{ - switch (type) - { - case vertex::DATA_SNORM8: - return data + readSNormData(L, components, data); - case vertex::DATA_UNORM8: - return data + readUNormData(L, components, data); - case vertex::DATA_INT8: - return data + readData(L, components, data); - case vertex::DATA_UINT8: - return data + readData(L, components, data); - case vertex::DATA_SNORM16: - return data + readSNormData(L, components, data); - case vertex::DATA_UNORM16: - return data + readUNormData(L, components, data); - case vertex::DATA_INT16: - return data + readData(L, components, data); - case vertex::DATA_UINT16: - return data + readData(L, components, data); - case vertex::DATA_INT32: - return data + readData(L, components, data); - case vertex::DATA_UINT32: - return data + readData(L, components, data); - case vertex::DATA_FLOAT: - return data + readData(L, components, data); - default: - return data; - } -} - int w_Mesh_setVertices(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); @@ -188,7 +55,7 @@ int w_Mesh_setVertices(lua_State *L) size_t byteoffset = vertstart * stride; int totalverts = (int) t->getVertexCount(); - if (vertstart >= totalverts) + if (vertstart >= totalverts || vertstart < 0) return luaL_error(L, "Invalid vertex start index (must be between 1 and %d)", totalverts); if (luax_istype(L, 2, Data::type)) @@ -215,11 +82,11 @@ int w_Mesh_setVertices(lua_State *L) if (vertstart + vertcount > totalverts) return luaL_error(L, "Too many vertices (expected at most %d, got %d)", totalverts - vertstart, vertcount); - const std::vector &vertexformat = t->getVertexFormat(); + const std::vector &vertexformat = t->getVertexFormat(); int ncomponents = 0; - for (const Mesh::AttribFormat &format : vertexformat) - ncomponents += format.components; + for (const Buffer::DataMember &member : vertexformat) + ncomponents += member.info.components; char *data = (char *) t->mapVertexData() + byteoffset; @@ -235,14 +102,16 @@ int w_Mesh_setVertices(lua_State *L) int idx = -ncomponents; - for (const Mesh::AttribFormat &format : vertexformat) + for (const Buffer::DataMember &member : vertexformat) { // Fetch the values from Lua and store them in data buffer. - data = luax_writeAttributeData(L, idx, format.type, format.components, data); - idx += format.components; + luax_writebufferdata(L, idx, member.decl.format, data + member.offset); + idx += member.info.components; } lua_pop(L, ncomponents + 1); + + data += stride; } t->unmapVertexData(byteoffset, vertcount * stride); @@ -256,34 +125,34 @@ int w_Mesh_setVertex(lua_State *L) bool istable = lua_istable(L, 3); - const std::vector &vertexformat = t->getVertexFormat(); - + const std::vector &vertexformat = t->getVertexFormat(); char *data = (char *) t->getVertexScratchBuffer(); - char *writtendata = data; int idx = istable ? 1 : 3; if (istable) { - for (const Mesh::AttribFormat &format : vertexformat) + for (const Buffer::DataMember &member : vertexformat) { - for (int i = idx; i < idx + format.components; i++) + int components = member.info.components; + + for (int i = idx; i < idx + components; i++) lua_rawgeti(L, 3, i); // Fetch the values from Lua and store them in data buffer. - writtendata = luax_writeAttributeData(L, -format.components, format.type, format.components, writtendata); + luax_writebufferdata(L, -components, member.decl.format, data + member.offset); - idx += format.components; - lua_pop(L, format.components); + idx += components; + lua_pop(L, components); } } else { - for (const Mesh::AttribFormat &format : vertexformat) + for (const Buffer::DataMember &member : vertexformat) { // Fetch the values from Lua and store them in data buffer. - writtendata = luax_writeAttributeData(L, idx, format.type, format.components, writtendata); - idx += format.components; + luax_writebufferdata(L, idx, member.decl.format, data + member.offset); + idx += member.info.components; } } @@ -296,19 +165,18 @@ int w_Mesh_getVertex(lua_State *L) Mesh *t = luax_checkmesh(L, 1); size_t index = (size_t) luaL_checkinteger(L, 2) - 1; - const std::vector &vertexformat = t->getVertexFormat(); + const std::vector &vertexformat = t->getVertexFormat(); char *data = (char *) t->getVertexScratchBuffer(); - const char *readdata = data; luax_catchexcept(L, [&](){ t->getVertex(index, data, t->getVertexStride()); }); int n = 0; - for (const Mesh::AttribFormat &format : vertexformat) + for (const Buffer::DataMember &member : vertexformat) { - readdata = luax_readAttributeData(L, format.type, format.components, readdata); - n += format.components; + luax_readbufferdata(L, member.decl.format, data + member.offset); + n += member.info.components; } return n; @@ -320,15 +188,18 @@ int w_Mesh_setVertexAttribute(lua_State *L) size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1; int attribindex = (int) luaL_checkinteger(L, 3) - 1; - vertex::DataType type; - int components; - luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); }); + const auto &vertexformat = t->getVertexFormat(); + + if (attribindex < 0 || attribindex >= (int) vertexformat.size()) + return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1); + + const Buffer::DataMember &member = vertexformat[attribindex]; // Maximum possible size for a single vertex attribute. char data[sizeof(float) * 4]; // Fetch the values from Lua and store them in the data buffer. - luax_writeAttributeData(L, 4, type, components, data); + luax_writebufferdata(L, 4, member.decl.format, data); luax_catchexcept(L, [&](){ t->setVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); }); return 0; @@ -340,17 +211,20 @@ int w_Mesh_getVertexAttribute(lua_State *L) size_t vertindex = (size_t) luaL_checkinteger(L, 2) - 1; int attribindex = (int) luaL_checkinteger(L, 3) - 1; - vertex::DataType type; - int components; - luax_catchexcept(L, [&](){ type = t->getAttributeInfo(attribindex, components); }); + const auto &vertexformat = t->getVertexFormat(); + + if (attribindex < 0 || attribindex >= (int) vertexformat.size()) + return luaL_error(L, "Invalid vertex attribute index: %d", attribindex + 1); + + const Buffer::DataMember &member = vertexformat[attribindex]; // Maximum possible size for a single vertex attribute. char data[sizeof(float) * 4]; luax_catchexcept(L, [&](){ t->getVertexAttribute(vertindex, attribindex, data, sizeof(float) * 4); }); - luax_readAttributeData(L, type, components, data); - return components; + luax_readbufferdata(L, member.decl.format, data); + return member.info.components; } int w_Mesh_getVertexCount(lua_State *L) @@ -364,28 +238,27 @@ int w_Mesh_getVertexFormat(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); - const std::vector &vertexformat = t->getVertexFormat(); + const std::vector &vertexformat = t->getVertexFormat(); lua_createtable(L, (int) vertexformat.size(), 0); const char *tname = nullptr; for (size_t i = 0; i < vertexformat.size(); i++) { - if (!vertex::getConstant(vertexformat[i].type, tname)) - return luax_enumerror(L, "vertex attribute data type", vertex::getConstants(vertexformat[i].type), tname); + const auto &decl = vertexformat[i].decl; + + if (!getConstant(decl.format, tname)) + return luax_enumerror(L, "vertex attribute data type", getConstants(decl.format), tname); lua_createtable(L, 3, 0); - lua_pushstring(L, vertexformat[i].name.c_str()); + lua_pushstring(L, decl.name.c_str()); lua_rawseti(L, -2, 1); lua_pushstring(L, tname); lua_rawseti(L, -2, 2); - lua_pushinteger(L, vertexformat[i].components); - lua_rawseti(L, -2, 3); - - // format[i] = {name, type, components} + // format[i] = {name, type} lua_rawseti(L, -2, (int) i + 1); } @@ -415,16 +288,29 @@ int w_Mesh_attachAttribute(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); const char *name = luaL_checkstring(L, 2); - Mesh *mesh = luax_checkmesh(L, 3); + + Buffer *buffer = nullptr; + if (luax_istype(L, 3, Buffer::type)) + { + buffer = luax_checktype(L, 3); + } + else + { + Mesh *mesh = luax_checkmesh(L, 3); + buffer = mesh->getVertexBuffer(); + if (buffer == nullptr) + return luaL_error(L, "Mesh does not have its own vertex buffer."); + luax_markdeprecated(L, "Mesh:attachAttribute(name, mesh, ...)", API_METHOD, DEPRECATED_REPLACED, "Mesh:attachAttribute(name, buffer, ...)"); + } AttributeStep step = STEP_PER_VERTEX; const char *stepstr = lua_isnoneornil(L, 4) ? nullptr : luaL_checkstring(L, 4); - if (stepstr != nullptr && !vertex::getConstant(stepstr, step)) - return luax_enumerror(L, "vertex attribute step", vertex::getConstants(step), stepstr); + if (stepstr != nullptr && !getConstant(stepstr, step)) + return luax_enumerror(L, "vertex attribute step", getConstants(step), stepstr); const char *attachname = luaL_optstring(L, 5, name); - luax_catchexcept(L, [&](){ t->attachAttribute(name, mesh, attachname, step); }); + luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer, attachname, step); }); return 0; } @@ -438,6 +324,48 @@ int w_Mesh_detachAttribute(lua_State *L) return 1; } +int w_Mesh_getAttachedAttributes(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + const auto &attributes = t->getAttachedAttributes(); + + lua_createtable(L, (int) attributes.size(), 0); + + for (int i = 0; i < (int) attributes.size(); i++) + { + const auto &attrib = attributes[i]; + + lua_createtable(L, 4, 0); + + luax_pushstring(L, attrib.name); + lua_rawseti(L, -1, 1); + + luax_pushtype(L, attrib.buffer.get()); + lua_rawseti(L, -1, 2); + + const char *stepstr = nullptr; + if (!getConstant(attrib.step, stepstr)) + return luaL_error(L, "Invalid vertex attribute step."); + lua_pushstring(L, stepstr); + lua_rawseti(L, -1, 3); + + const Buffer::DataMember &member = attrib.buffer->getDataMember(attrib.indexInBuffer); + luax_pushstring(L, member.decl.name); + lua_rawseti(L, -1, 4); + + lua_rawseti(L, -1, i + 1); + } + + return 1; +} + +int w_Mesh_getVertexBuffer(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + luax_pushtype(L, t->getVertexBuffer()); + return 1; +} + int w_Mesh_flush(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); @@ -462,10 +390,10 @@ int w_Mesh_setVertexMap(lua_State *L) const char *indextypestr = luaL_checkstring(L, 3); IndexDataType indextype; - if (!vertex::getConstant(indextypestr, indextype)) - return luax_enumerror(L, "index data type", vertex::getConstants(indextype), indextypestr); + if (!getConstant(indextypestr, indextype)) + return luax_enumerror(L, "index data type", getConstants(indextype), indextypestr); - size_t datatypesize = vertex::getIndexDataSize(indextype); + size_t datatypesize = getIndexDataSize(indextype); int indexcount = (int) luaL_optinteger(L, 4, d->getSize() / datatypesize); @@ -528,6 +456,23 @@ int w_Mesh_getVertexMap(lua_State *L) return 1; } +int w_Mesh_setIndexBuffer(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + Buffer *b = nullptr; + if (!lua_isnoneornil(L, 2)) + b = luax_checkbuffer(L, 2); + luax_catchexcept(L, [&]() { t->setIndexBuffer(b); }); + return 0; +} + +int w_Mesh_getIndexBuffer(lua_State *L) +{ + Mesh *t = luax_checkmesh(L, 1); + luax_pushtype(L, t->getIndexBuffer()); + return 1; +} + int w_Mesh_setTexture(lua_State *L) { Mesh *t = luax_checkmesh(L, 1); @@ -561,8 +506,8 @@ int w_Mesh_setDrawMode(lua_State *L) const char *str = luaL_checkstring(L, 2); PrimitiveType mode; - if (!vertex::getConstant(str, mode)) - return luax_enumerror(L, "mesh draw mode", vertex::getConstants(mode), str); + if (!getConstant(str, mode)) + return luax_enumerror(L, "mesh draw mode", getConstants(mode), str); t->setDrawMode(mode); return 0; @@ -574,7 +519,7 @@ int w_Mesh_getDrawMode(lua_State *L) PrimitiveType mode = t->getDrawMode(); const char *str; - if (!vertex::getConstant(mode, str)) + if (!getConstant(mode, str)) return luaL_error(L, "Unknown mesh draw mode."); lua_pushstring(L, str); @@ -624,9 +569,13 @@ static const luaL_Reg w_Mesh_functions[] = { "isAttributeEnabled", w_Mesh_isAttributeEnabled }, { "attachAttribute", w_Mesh_attachAttribute }, { "detachAttribute", w_Mesh_detachAttribute }, + { "getAttachedAttributes", w_Mesh_getAttachedAttributes }, + { "getVertexBuffer", w_Mesh_getVertexBuffer }, { "flush", w_Mesh_flush }, { "setVertexMap", w_Mesh_setVertexMap }, { "getVertexMap", w_Mesh_getVertexMap }, + { "setIndexBuffer", w_Mesh_setIndexBuffer }, + { "getIndexBuffer", w_Mesh_getIndexBuffer }, { "setTexture", w_Mesh_setTexture }, { "getTexture", w_Mesh_getTexture }, { "setDrawMode", w_Mesh_setDrawMode }, diff --git a/src/modules/graphics/wrap_Mesh.h b/src/modules/graphics/wrap_Mesh.h index 34a18b1b8..08f73f2e9 100644 --- a/src/modules/graphics/wrap_Mesh.h +++ b/src/modules/graphics/wrap_Mesh.h @@ -30,9 +30,6 @@ namespace love namespace graphics { -char *luax_writeAttributeData(lua_State *L, int startidx, vertex::DataType type, int components, char *data); -const char *luax_readAttributeData(lua_State *L, vertex::DataType type, int components, const char *data); - Mesh *luax_checkmesh(lua_State *L, int idx); extern "C" int luaopen_mesh(lua_State *L); diff --git a/src/modules/graphics/wrap_SpriteBatch.cpp b/src/modules/graphics/wrap_SpriteBatch.cpp index 420ece908..981bcb6dc 100644 --- a/src/modules/graphics/wrap_SpriteBatch.cpp +++ b/src/modules/graphics/wrap_SpriteBatch.cpp @@ -216,9 +216,22 @@ int w_SpriteBatch_attachAttribute(lua_State *L) { SpriteBatch *t = luax_checkspritebatch(L, 1); const char *name = luaL_checkstring(L, 2); - Mesh *m = luax_checktype(L, 3); - luax_catchexcept(L, [&](){ t->attachAttribute(name, m); }); + Buffer *buffer = nullptr; + if (luax_istype(L, 3, Buffer::type)) + { + buffer = luax_checktype(L, 3); + } + else + { + Mesh *mesh = luax_checktype(L, 3); + buffer = mesh->getVertexBuffer(); + if (buffer == nullptr) + return luaL_error(L, "Mesh does not have its own vertex buffer."); + luax_markdeprecated(L, "SpriteBatch:attachAttribute(name, mesh)", API_METHOD, DEPRECATED_REPLACED, "SpriteBatch:attachAttribute(name, buffer)"); + } + + luax_catchexcept(L, [&](){ t->attachAttribute(name, buffer); }); return 0; }