Browse Source

metal: hook up emulated 'fan' mesh draw mode.

Alex Szpakowski 3 years ago
parent
commit
62de8f62c7

+ 6 - 0
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);

+ 33 - 2
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])

+ 3 - 0
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;
 

+ 36 - 23
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())
+	{
+		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)
 	{
-		Graphics::DrawIndexedCommand cmd(&attributes, &buffers, indexBuffer);
+		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();

+ 1 - 2
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> texture;
 

+ 4 - 3
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<OptionalColorD> &colors, OptionalInt sten
 }}
 
 void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
-{
+{ @autoreleasepool {
 	flushBatchedDraws();
 
 	// TODO
@@ -1497,7 +1498,7 @@ void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
 		passDesc.stencilAttachment.loadAction = MTLLoadActionDontCare;
 		passDesc.depthAttachment.loadAction = MTLLoadActionDontCare;
 	}
-}
+}}
 
 void Graphics::present(void *screenshotCallbackData)
 { @autoreleasepool {