diff --git a/src/common/Range.h b/src/common/Range.h index 1a70c64fb..c37542211 100644 --- a/src/common/Range.h +++ b/src/common/Range.h @@ -66,6 +66,12 @@ struct Range return !(first > other.last || last < other.first); } + void intersect(const Range &other) + { + first = std::max(first, other.first); + last = std::min(last, other.last); + } + void encapsulate(size_t index) { first = std::min(first, index); diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index 38ad9a07a..f9ee6532f 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -184,6 +184,7 @@ Graphics::Graphics() , drawCalls(0) , drawCallsBatched(0) , quadIndexBuffer(nullptr) + , fanIndexBuffer(nullptr) , capabilities() , cachedShaderStages() { @@ -202,7 +203,10 @@ Graphics::Graphics() Graphics::~Graphics() { - delete quadIndexBuffer; + if (quadIndexBuffer != nullptr) + quadIndexBuffer->release(); + if (fanIndexBuffer != nullptr) + fanIndexBuffer->release(); // Clean up standard shaders before the active shader. If we do it after, // the active shader may try to activate a standard shader when deactivating @@ -239,7 +243,7 @@ void Graphics::createQuadIndexBuffer() if (quadIndexBuffer != nullptr) return; - size_t size = sizeof(uint16) * (LOVE_UINT16_MAX / 4) * 6; + size_t size = sizeof(uint16) * getIndexCount(TRIANGLEINDEX_QUADS, LOVE_UINT16_MAX); Buffer::Settings settings(BUFFERUSAGEFLAG_INDEX, BUFFERDATAUSAGE_STATIC); quadIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0); @@ -250,6 +254,22 @@ void Graphics::createQuadIndexBuffer() quadIndexBuffer->setImmutable(true); } +void Graphics::createFanIndexBuffer() +{ + if (fanIndexBuffer != nullptr) + return; + + size_t size = sizeof(uint16) * getIndexCount(TRIANGLEINDEX_FAN, LOVE_UINT16_MAX); + + Buffer::Settings settings(BUFFERUSAGEFLAG_INDEX, BUFFERDATAUSAGE_STATIC); + fanIndexBuffer = newBuffer(settings, DATAFORMAT_UINT16, nullptr, size, 0); + + Buffer::Mapper map(*fanIndexBuffer); + fillIndices(TRIANGLEINDEX_FAN, 0, LOVE_UINT16_MAX, (uint16 *) map.data); + + fanIndexBuffer->setImmutable(true); +} + Quad *Graphics::newQuad(Quad::Viewport v, double sw, double sh) { return new Quad(v, sw, sh); @@ -1617,6 +1637,17 @@ void Graphics::drawInstanced(Mesh *mesh, const Matrix4 &m, int instancecount) void Graphics::drawShaderVertices(PrimitiveType primtype, int vertexcount, int instancecount, Texture *maintexture) { + if (primtype == PRIMITIVE_TRIANGLE_FAN && vertexcount > LOVE_UINT16_MAX) + throw love::Exception("drawShaderVertices cannot draw more than %d vertices when the 'fan' draw mode is used.", LOVE_UINT16_MAX); + + // Emulated triangle fan via an index buffer. + if (primtype == PRIMITIVE_TRIANGLE_FAN && getFanIndexBuffer()) + { + int indexcount = getIndexCount(TRIANGLEINDEX_FAN, vertexcount); + drawShaderVertices(getFanIndexBuffer(), indexcount, instancecount, 0, maintexture); + return; + } + flushBatchedDraws(); if (!capabilities.features[FEATURE_GLSL3]) diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index d3fb68750..f318ca78e 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -526,6 +526,7 @@ public: virtual int getBackbufferMSAA() const = 0; Buffer *getQuadIndexBuffer() const { return quadIndexBuffer; } + Buffer *getFanIndexBuffer() const { return fanIndexBuffer; } /** * Sets the current constant color. @@ -987,6 +988,7 @@ protected: virtual void getAPIStats(int &shaderswitches) const = 0; void createQuadIndexBuffer(); + void createFanIndexBuffer(); Texture *getTemporaryTexture(PixelFormat format, int w, int h, int samples); @@ -1029,6 +1031,7 @@ protected: int drawCallsBatched; Buffer *quadIndexBuffer; + Buffer *fanIndexBuffer; Capabilities capabilities; diff --git a/src/modules/graphics/Mesh.cpp b/src/modules/graphics/Mesh.cpp index adbb87e2a..adbde8a4d 100644 --- a/src/modules/graphics/Mesh.cpp +++ b/src/modules/graphics/Mesh.cpp @@ -501,22 +501,21 @@ void Mesh::setDrawRange(int start, int count) if (start < 0 || count <= 0) throw love::Exception("Invalid draw range."); - rangeStart = start; - rangeCount = count; + drawRange = Range(start, count); } void Mesh::setDrawRange() { - rangeStart = rangeCount = -1; + drawRange.invalidate(); } bool Mesh::getDrawRange(int &start, int &count) const { - if (rangeStart < 0 || rangeCount <= 0) + if (!drawRange.isValid()) return false; - start = rangeStart; - count = rangeCount; + start = (int) drawRange.getOffset(); + count = (int) drawRange.getSize(); return true; } @@ -595,9 +594,30 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) Graphics::TempTransform transform(gfx, m); - if (useIndexBuffer && indexBuffer != nullptr && indexCount > 0) + Buffer *indexbuffer = useIndexBuffer ? indexBuffer : nullptr; + int indexcount = (int) indexCount; + Range range = drawRange; + + // Emulated triangle fan via an index buffer. + if (primitiveType == PRIMITIVE_TRIANGLE_FAN && indexbuffer == nullptr && gfx->getFanIndexBuffer()) { - Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexBuffer); + indexbuffer = gfx->getFanIndexBuffer(); + indexcount = graphics::getIndexCount(TRIANGLEINDEX_FAN, vertexCount); + if (range.isValid()) + { + int start = graphics::getIndexCount(TRIANGLEINDEX_FAN, (int) range.getOffset()); + int count = graphics::getIndexCount(TRIANGLEINDEX_FAN, (int) range.getSize()); + range = Range(start, count); + } + } + + if (indexbuffer != nullptr && indexcount > 0) + { + Range r(0, indexcount); + if (range.isValid()) + r.intersect(range); + + Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexbuffer); cmd.primitiveType = primitiveType; cmd.indexType = indexDataType; @@ -605,30 +625,23 @@ void Mesh::drawInstanced(Graphics *gfx, const Matrix4 &m, int instancecount) cmd.texture = texture; cmd.cullMode = gfx->getMeshCullMode(); - int start = std::min(std::max(0, rangeStart), (int) indexCount - 1); - cmd.indexBufferOffset = start * getIndexDataSize(indexDataType); - - cmd.indexCount = (int) indexCount; - if (rangeCount > 0) - cmd.indexCount = std::min(cmd.indexCount, rangeCount); - - cmd.indexCount = std::min(cmd.indexCount, (int) indexCount - start); + cmd.indexBufferOffset = r.getOffset() * indexbuffer->getArrayStride(); + cmd.indexCount = (int) r.getSize(); if (cmd.indexCount > 0) gfx->draw(cmd); } else if (vertexCount > 0) { + Range r(0, vertexCount); + if (range.isValid()) + r.intersect(range); + Graphics::DrawCommand cmd(&attributes, &buffers); cmd.primitiveType = primitiveType; - cmd.vertexStart = std::min(std::max(0, rangeStart), (int) vertexCount - 1); - - cmd.vertexCount = (int) vertexCount; - if (rangeCount > 0) - cmd.vertexCount = std::min(cmd.vertexCount, rangeCount); - - cmd.vertexCount = std::min(cmd.vertexCount, (int) vertexCount - cmd.vertexStart); + cmd.vertexStart = (int) r.getOffset(); + cmd.vertexCount = (int) r.getSize(); cmd.instanceCount = instancecount; cmd.texture = texture; cmd.cullMode = gfx->getMeshCullMode(); diff --git a/src/modules/graphics/Mesh.h b/src/modules/graphics/Mesh.h index b40364786..a3a67fa2f 100644 --- a/src/modules/graphics/Mesh.h +++ b/src/modules/graphics/Mesh.h @@ -207,8 +207,7 @@ private: PrimitiveType primitiveType = PRIMITIVE_TRIANGLES; - int rangeStart = -1; - int rangeCount = -1; + Range drawRange = Range(); StrongRef texture; diff --git a/src/modules/graphics/metal/Graphics.mm b/src/modules/graphics/metal/Graphics.mm index d680c25a9..05481b58c 100644 --- a/src/modules/graphics/metal/Graphics.mm +++ b/src/modules/graphics/metal/Graphics.mm @@ -118,7 +118,7 @@ static MTLPrimitiveType getMTLPrimitiveType(PrimitiveType prim) { case PRIMITIVE_TRIANGLES: return MTLPrimitiveTypeTriangle; case PRIMITIVE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; - case PRIMITIVE_TRIANGLE_FAN: return MTLPrimitiveTypeTriangle; // TODO: This needs to be emulated. + case PRIMITIVE_TRIANGLE_FAN: return MTLPrimitiveTypeTriangle; // This is emulated with an index buffer. case PRIMITIVE_POINTS: return MTLPrimitiveTypePoint; case PRIMITIVE_MAX_ENUM: return MTLPrimitiveTypeTriangle; } @@ -336,6 +336,7 @@ Graphics::Graphics() } createQuadIndexBuffer(); + createFanIndexBuffer(); // We always need a default shader. for (int i = 0; i < Shader::STANDARD_MAX_ENUM; i++) @@ -1476,7 +1477,7 @@ void Graphics::clear(const std::vector &colors, OptionalInt sten }} void Graphics::discard(const std::vector &colorbuffers, bool depthstencil) -{ +{ @autoreleasepool { flushBatchedDraws(); // TODO @@ -1497,7 +1498,7 @@ void Graphics::discard(const std::vector &colorbuffers, bool depthstencil) passDesc.stencilAttachment.loadAction = MTLLoadActionDontCare; passDesc.depthAttachment.loadAction = MTLLoadActionDontCare; } -} +}} void Graphics::present(void *screenshotCallbackData) { @autoreleasepool {