Browse Source

Added love.graphics.getStats. It returns a table with performance-related graphics statistics. Currently it contains these fields:

drawcalls: The number of internal draw calls made so far in the current frame.

canvasswitches: The number of times the active Canvas has been changed so far in the current frame.

texturememory: The approximate amount of video memory (in bytes) used by OpenGL textures created by Images, Canvases, and Fonts.

images: The number of active Image objects.

canvases: The number of active Canvas objects.

fonts: The number of active Font objects.
Alex Szpakowski 11 years ago
parent
commit
8ffe610659

+ 22 - 0
src/modules/graphics/Graphics.cpp

@@ -119,6 +119,16 @@ bool Graphics::getConstant(StackType in, const char *&out)
 	return stackTypes.find(in, out);
 	return stackTypes.find(in, out);
 }
 }
 
 
+bool Graphics::getConstant(const char *in, StatType &out)
+{
+	return statTypes.find(in, out);
+}
+
+bool Graphics::getConstant(StatType in, const char *&out)
+{
+	return statTypes.find(in, out);
+}
+
 StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
 StringMap<Graphics::DrawMode, Graphics::DRAW_MAX_ENUM>::Entry Graphics::drawModeEntries[] =
 {
 {
 	{ "line", Graphics::DRAW_LINE },
 	{ "line", Graphics::DRAW_LINE },
@@ -210,5 +220,17 @@ StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM>::Entry Graphics::stackT
 
 
 StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM> Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries));
 StringMap<Graphics::StackType, Graphics::STACK_MAX_ENUM> Graphics::stackTypes(Graphics::stackTypeEntries, sizeof(Graphics::stackTypeEntries));
 
 
+StringMap<Graphics::StatType, Graphics::STAT_MAX_ENUM>::Entry Graphics::statTypeEntries[] =
+{
+	{"drawcalls", Graphics::STAT_DRAW_CALLS},
+	{"canvasswitches", Graphics::STAT_CANVAS_SWITCHES},
+	{"canvases", Graphics::STAT_CANVASES},
+	{"images", Graphics::STAT_IMAGES},
+	{"fonts", Graphics::STAT_FONTS},
+	{"texturememory", Graphics::STAT_TEXTURE_MEMORY},
+};
+
+StringMap<Graphics::StatType, Graphics::STAT_MAX_ENUM> Graphics::statTypes(Graphics::statTypeEntries, sizeof(Graphics::statTypeEntries));
+
 } // graphics
 } // graphics
 } // love
 } // love

+ 27 - 0
src/modules/graphics/Graphics.h

@@ -119,6 +119,17 @@ public:
 		STACK_MAX_ENUM
 		STACK_MAX_ENUM
 	};
 	};
 
 
+	enum StatType
+	{
+		STAT_DRAW_CALLS,
+		STAT_CANVAS_SWITCHES,
+		STAT_CANVASES,
+		STAT_IMAGES,
+		STAT_FONTS,
+		STAT_TEXTURE_MEMORY,
+		STAT_MAX_ENUM
+	};
+
 	struct RendererInfo
 	struct RendererInfo
 	{
 	{
 		std::string name;
 		std::string name;
@@ -127,6 +138,16 @@ public:
 		std::string device;
 		std::string device;
 	};
 	};
 
 
+	struct Stats
+	{
+		int drawCalls;
+		int canvasSwitches;
+		int canvases;
+		int images;
+		int fonts;
+		size_t textureMemory;
+	};
+
 	virtual ~Graphics();
 	virtual ~Graphics();
 
 
 	// Implements Module.
 	// Implements Module.
@@ -177,6 +198,9 @@ public:
 	static bool getConstant(const char *in, StackType &out);
 	static bool getConstant(const char *in, StackType &out);
 	static bool getConstant(StackType in, const char *&out);
 	static bool getConstant(StackType in, const char *&out);
 
 
+	static bool getConstant(const char *in, StatType &out);
+	static bool getConstant(StatType in, const char *&out);
+
 private:
 private:
 
 
 	static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
 	static StringMap<DrawMode, DRAW_MAX_ENUM>::Entry drawModeEntries[];
@@ -206,6 +230,9 @@ private:
 	static StringMap<StackType, STACK_MAX_ENUM>::Entry stackTypeEntries[];
 	static StringMap<StackType, STACK_MAX_ENUM>::Entry stackTypeEntries[];
 	static StringMap<StackType, STACK_MAX_ENUM> stackTypes;
 	static StringMap<StackType, STACK_MAX_ENUM> stackTypes;
 
 
+	static StringMap<StatType, STAT_MAX_ENUM>::Entry statTypeEntries[];
+	static StringMap<StatType, STAT_MAX_ENUM> statTypes;
+
 }; // Graphics
 }; // Graphics
 
 
 } // graphics
 } // graphics

+ 42 - 1
src/modules/graphics/opengl/Canvas.cpp

@@ -185,6 +185,7 @@ struct FramebufferStrategyGL3 : public FramebufferStrategy
 	virtual void bindFBO(GLuint framebuffer)
 	virtual void bindFBO(GLuint framebuffer)
 	{
 	{
 		glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
 		glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+		++Canvas::switchCount;
 	}
 	}
 
 
 	virtual void setAttachments()
 	virtual void setAttachments()
@@ -314,6 +315,7 @@ struct FramebufferStrategyPackedEXT : public FramebufferStrategy
 	virtual void bindFBO(GLuint framebuffer)
 	virtual void bindFBO(GLuint framebuffer)
 	{
 	{
 		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
 		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer);
+		++Canvas::switchCount;
 	}
 	}
 
 
 	virtual void setAttachments()
 	virtual void setAttachments()
@@ -407,6 +409,8 @@ FramebufferStrategyEXT strategyEXT;
 Canvas *Canvas::current = nullptr;
 Canvas *Canvas::current = nullptr;
 OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
 OpenGL::Viewport Canvas::systemViewport = OpenGL::Viewport();
 bool Canvas::screenHasSRGB = false;
 bool Canvas::screenHasSRGB = false;
+int Canvas::switchCount = 0;
+int Canvas::canvasCount = 0;
 
 
 static void getStrategy()
 static void getStrategy()
 {
 {
@@ -432,6 +436,7 @@ Canvas::Canvas(int width, int height, Format format, int msaa)
 	, format(format)
 	, format(format)
     , msaa_samples(msaa)
     , msaa_samples(msaa)
 	, msaa_dirty(false)
 	, msaa_dirty(false)
+	, texture_memory(0)
 {
 {
 	this->width = width;
 	this->width = width;
 	this->height = height;
 	this->height = height;
@@ -462,10 +467,14 @@ Canvas::Canvas(int width, int height, Format format, int msaa)
 	getStrategy();
 	getStrategy();
 
 
 	loadVolatile();
 	loadVolatile();
+
+	++canvasCount;
 }
 }
 
 
 Canvas::~Canvas()
 Canvas::~Canvas()
 {
 {
+	--canvasCount;
+
 	// reset framebuffer if still using this one
 	// reset framebuffer if still using this one
 	if (current == this)
 	if (current == this)
 		stopGrab();
 		stopGrab();
@@ -581,6 +590,14 @@ bool Canvas::loadVolatile()
 
 
 	msaa_dirty = (msaa_buffer != 0);
 	msaa_dirty = (msaa_buffer != 0);
 
 
+	size_t prevmemsize = texture_memory;
+
+	texture_memory = (getFormatBitsPerPixel(format) * width * height) / 8;
+	if (msaa_buffer != 0)
+		texture_memory += (texture_memory * msaa_samples);
+
+	gl.updateTextureMemorySize(prevmemsize, texture_memory);
+
 	return true;
 	return true;
 }
 }
 
 
@@ -595,6 +612,9 @@ void Canvas::unloadVolatile()
 	resolve_fbo = msaa_buffer = 0;
 	resolve_fbo = msaa_buffer = 0;
 
 
 	attachedCanvases.clear();
 	attachedCanvases.clear();
+
+	gl.updateTextureMemorySize(texture_memory, 0);
+	texture_memory = 0;
 }
 }
 
 
 void Canvas::drawv(const Matrix &t, const Vertex *v)
 void Canvas::drawv(const Matrix &t, const Vertex *v)
@@ -611,7 +631,7 @@ void Canvas::drawv(const Matrix &t, const Vertex *v)
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
 
 
 	gl.prepareDraw();
 	gl.prepareDraw();
-	glDrawArrays(GL_QUADS, 0, 4);
+	gl.drawArrays(GL_QUADS, 0, 4);
 
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
@@ -1012,6 +1032,27 @@ void Canvas::convertFormat(Canvas::Format format, GLenum &internalformat, GLenum
 	}
 	}
 }
 }
 
 
+size_t Canvas::getFormatBitsPerPixel(Format format)
+{
+	switch (getSizedFormat(format))
+	{
+	case FORMAT_RGBA8:
+	case FORMAT_RGB10A2:
+	case FORMAT_RG11B10F:
+	case FORMAT_SRGB:
+	default:
+		return 32;
+	case FORMAT_RGBA4:
+	case FORMAT_RGB5A1:
+	case FORMAT_RGB565:
+		return 16;
+	case FORMAT_RGBA16F:
+		return 64;
+	case FORMAT_RGBA32F:
+		return 128;
+	}
+}
+
 bool Canvas::isSupported()
 bool Canvas::isSupported()
 {
 {
 	getStrategy();
 	getStrategy();

+ 7 - 0
src/modules/graphics/opengl/Canvas.h

@@ -26,6 +26,7 @@
 #include "image/ImageData.h"
 #include "image/ImageData.h"
 #include "common/Matrix.h"
 #include "common/Matrix.h"
 #include "common/StringMap.h"
 #include "common/StringMap.h"
+#include "common/int.h"
 #include "Texture.h"
 #include "Texture.h"
 #include "OpenGL.h"
 #include "OpenGL.h"
 
 
@@ -127,6 +128,9 @@ public:
 	// Whether the main screen should have linear -> sRGB conversions enabled.
 	// Whether the main screen should have linear -> sRGB conversions enabled.
 	static bool screenHasSRGB;
 	static bool screenHasSRGB;
 
 
+	static int switchCount;
+	static int canvasCount;
+
 	static bool getConstant(const char *in, Format &out);
 	static bool getConstant(const char *in, Format &out);
 	static bool getConstant(Format in, const char *&out);
 	static bool getConstant(Format in, const char *&out);
 
 
@@ -136,6 +140,7 @@ private:
 
 
 	static Format getSizedFormat(Format format);
 	static Format getSizedFormat(Format format);
 	static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
 	static void convertFormat(Format format, GLenum &internalformat, GLenum &externalformat, GLenum &type);
+	static size_t getFormatBitsPerPixel(Format format);
 
 
 	GLuint fbo;
 	GLuint fbo;
 	GLuint resolve_fbo;
 	GLuint resolve_fbo;
@@ -153,6 +158,8 @@ private:
 	int msaa_samples;
 	int msaa_samples;
 	bool msaa_dirty;
 	bool msaa_dirty;
 
 
+	size_t texture_memory;
+
 	void setupGrab();
 	void setupGrab();
 	void drawv(const Matrix &t, const Vertex *v);
 	void drawv(const Matrix &t, const Vertex *v);
 
 

+ 16 - 1
src/modules/graphics/opengl/Font.cpp

@@ -37,6 +37,8 @@ namespace graphics
 namespace opengl
 namespace opengl
 {
 {
 
 
+int Font::fontCount = 0;
+
 const int Font::TEXTURE_WIDTHS[]  = {128, 256, 256, 512, 512, 1024, 1024};
 const int Font::TEXTURE_WIDTHS[]  = {128, 256, 256, 512, 512, 1024, 1024};
 const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512,  1024};
 const int Font::TEXTURE_HEIGHTS[] = {128, 128, 256, 256, 512, 512,  1024};
 
 
@@ -47,6 +49,7 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
 	, mSpacing(1)
 	, mSpacing(1)
 	, filter(filter)
 	, filter(filter)
 	, useSpacesAsTab(false)
 	, useSpacesAsTab(false)
+	, textureMemorySize(0)
 {
 {
 	this->filter.mipmap = Texture::FILTER_NONE;
 	this->filter.mipmap = Texture::FILTER_NONE;
 
 
@@ -87,11 +90,15 @@ Font::Font(love::font::Rasterizer *r, const Texture::Filter &filter)
 	}
 	}
 
 
 	delete gd;
 	delete gd;
+
+	++fontCount;
 }
 }
 
 
 Font::~Font()
 Font::~Font()
 {
 {
 	unloadVolatile();
 	unloadVolatile();
+
+	--fontCount;
 }
 }
 
 
 bool Font::initializeTexture(GLenum format)
 bool Font::initializeTexture(GLenum format)
@@ -169,6 +176,11 @@ void Font::createTexture()
 					&emptyData[0]);
 					&emptyData[0]);
 
 
 	setFilter(filter);
 	setFilter(filter);
+
+	size_t prevmemsize = textureMemorySize;
+
+	textureMemorySize += emptyData.size();
+	gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
 }
 }
 
 
 Font::Glyph *Font::addGlyph(uint32 glyph)
 Font::Glyph *Font::addGlyph(uint32 glyph)
@@ -385,7 +397,7 @@ void Font::print(const std::string &text, float x, float y, float extra_spacing,
 	for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
 	for (it = glyphinfolist.begin(); it != glyphinfolist.end(); ++it)
 	{
 	{
 		gl.bindTexture(it->texture);
 		gl.bindTexture(it->texture);
-		glDrawArrays(GL_QUADS, it->startvertex, it->vertexcount);
+		gl.drawArrays(GL_QUADS, it->startvertex, it->vertexcount);
 	}
 	}
 
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
@@ -561,6 +573,9 @@ void Font::unloadVolatile()
 		iter++;
 		iter++;
 	}
 	}
 	textures.clear();
 	textures.clear();
+
+	gl.updateTextureMemorySize(textureMemorySize, 0);
+	textureMemorySize = 0;
 }
 }
 
 
 int Font::getAscent() const
 int Font::getAscent() const

+ 4 - 0
src/modules/graphics/opengl/Font.h

@@ -138,6 +138,8 @@ public:
 	bool hasGlyph(uint32 glyph) const;
 	bool hasGlyph(uint32 glyph) const;
 	bool hasGlyphs(const std::string &text) const;
 	bool hasGlyphs(const std::string &text) const;
 
 
+	static int fontCount;
+
 private:
 private:
 
 
 	enum FontType
 	enum FontType
@@ -207,6 +209,8 @@ private:
 
 
 	bool useSpacesAsTab;
 	bool useSpacesAsTab;
 
 
+	size_t textureMemorySize;
+
 	static const int NUM_TEXTURE_SIZES = 7;
 	static const int NUM_TEXTURE_SIZES = 7;
 	static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
 	static const int TEXTURE_WIDTHS[NUM_TEXTURE_SIZES];
 	static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];
 	static const int TEXTURE_HEIGHTS[NUM_TEXTURE_SIZES];

+ 22 - 2
src/modules/graphics/opengl/Graphics.cpp

@@ -350,6 +350,10 @@ void Graphics::clear()
 void Graphics::present()
 void Graphics::present()
 {
 {
 	currentWindow->swapBuffers();
 	currentWindow->swapBuffers();
+
+	// Reset the per-frame stat counts.
+	gl.stats.drawCalls = 0;
+	Canvas::switchCount = 0;
 }
 }
 
 
 int Graphics::getWidth() const
 int Graphics::getWidth() const
@@ -947,6 +951,8 @@ void Graphics::point(float x, float y)
 	glBegin(GL_POINTS);
 	glBegin(GL_POINTS);
 	glVertex2f(x, y);
 	glVertex2f(x, y);
 	glEnd();
 	glEnd();
+
+	++gl.stats.drawCalls;
 }
 }
 
 
 void Graphics::polyline(const float *coords, size_t count)
 void Graphics::polyline(const float *coords, size_t count)
@@ -1042,7 +1048,7 @@ void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1,
 		gl.bindTexture(0);
 		gl.bindTexture(0);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *) coords);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *) coords);
-		glDrawArrays(GL_TRIANGLE_FAN, 0, points + 2);
+		gl.drawArrays(GL_TRIANGLE_FAN, 0, points + 2);
 		glDisableClientState(GL_VERTEX_ARRAY);
 		glDisableClientState(GL_VERTEX_ARRAY);
 	}
 	}
 
 
@@ -1066,7 +1072,7 @@ void Graphics::polygon(DrawMode mode, const float *coords, size_t count)
 		gl.bindTexture(0);
 		gl.bindTexture(0);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glEnableClientState(GL_VERTEX_ARRAY);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)coords);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)coords);
-		glDrawArrays(GL_POLYGON, 0, count/2-1); // opengl will close the polygon for us
+		gl.drawArrays(GL_POLYGON, 0, count/2-1); // opengl will close the polygon for us
 		glDisableClientState(GL_VERTEX_ARRAY);
 		glDisableClientState(GL_VERTEX_ARRAY);
 	}
 	}
 }
 }
@@ -1165,6 +1171,20 @@ Graphics::RendererInfo Graphics::getRendererInfo() const
 	return info;
 	return info;
 }
 }
 
 
+Graphics::Stats Graphics::getStats() const
+{
+	Stats stats;
+
+	stats.drawCalls = gl.stats.drawCalls;
+	stats.canvasSwitches = Canvas::switchCount;
+	stats.canvases = Canvas::canvasCount;
+	stats.images = Image::imageCount;
+	stats.fonts = Font::fontCount;
+	stats.textureMemory = gl.stats.textureMemory;
+
+	return stats;
+}
+
 double Graphics::getSystemLimit(SystemLimit limittype) const
 double Graphics::getSystemLimit(SystemLimit limittype) const
 {
 {
 	double limit = 0.0;
 	double limit = 0.0;

+ 6 - 1
src/modules/graphics/opengl/Graphics.h

@@ -412,11 +412,16 @@ public:
 
 
 	/**
 	/**
 	 * Returns system-dependent renderer information.
 	 * Returns system-dependent renderer information.
-	 * Returned string s can vary greatly between systems! Do not rely on it for
+	 * Returned strings can vary greatly between systems! Do not rely on it for
 	 * anything!
 	 * anything!
 	 **/
 	 **/
 	RendererInfo getRendererInfo() const;
 	RendererInfo getRendererInfo() const;
 
 
+	/**
+	 * Returns performance-related statistics.
+	 **/
+	Stats getStats() const;
+
 	/**
 	/**
 	 * Gets the system-dependent numeric limit for the specified parameter.
 	 * Gets the system-dependent numeric limit for the specified parameter.
 	 **/
 	 **/

+ 34 - 1
src/modules/graphics/opengl/Image.cpp

@@ -31,6 +31,8 @@ namespace graphics
 namespace opengl
 namespace opengl
 {
 {
 
 
+int Image::imageCount = 0;
+
 float Image::maxMipmapSharpness = 0.0f;
 float Image::maxMipmapSharpness = 0.0f;
 
 
 Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
 Texture::FilterMode Image::defaultMipmapFilter = Texture::FILTER_NONE;
@@ -47,10 +49,14 @@ Image::Image(love::image::ImageData *data, Format format)
 	, compressed(false)
 	, compressed(false)
 	, format(format)
 	, format(format)
 	, usingDefaultTexture(false)
 	, usingDefaultTexture(false)
+	, textureMemorySize(0)
 {
 {
 	width = data->getWidth();
 	width = data->getWidth();
 	height = data->getHeight();
 	height = data->getHeight();
 	preload();
 	preload();
+
+	textureMemorySize = data->getSize();
+	++imageCount;
 }
 }
 
 
 Image::Image(love::image::CompressedData *cdata, Format format)
 Image::Image(love::image::CompressedData *cdata, Format format)
@@ -64,15 +70,21 @@ Image::Image(love::image::CompressedData *cdata, Format format)
 	, compressed(true)
 	, compressed(true)
 	, format(format)
 	, format(format)
 	, usingDefaultTexture(false)
 	, usingDefaultTexture(false)
+	, textureMemorySize(0)
 {
 {
 	width = cdata->getWidth(0);
 	width = cdata->getWidth(0);
 	height = cdata->getHeight(0);
 	height = cdata->getHeight(0);
 	preload();
 	preload();
+
+	textureMemorySize = cdata->getSize(0);
+	++imageCount;
 }
 }
 
 
 Image::~Image()
 Image::~Image()
 {
 {
 	unload();
 	unload();
+
+	--imageCount;
 }
 }
 
 
 love::image::ImageData *Image::getImageData() const
 love::image::ImageData *Image::getImageData() const
@@ -151,8 +163,11 @@ void Image::uploadCompressedMipmaps()
 		      "compressed image does not have all required levels.");
 		      "compressed image does not have all required levels.");
 	}
 	}
 
 
+	size_t totalsize = cdata->getSize(0);
+
 	for (int i = 1; i < count; i++)
 	for (int i = 1; i < count; i++)
 	{
 	{
+		totalsize += cdata->getSize(i);
 		glCompressedTexImage2DARB(GL_TEXTURE_2D,
 		glCompressedTexImage2DARB(GL_TEXTURE_2D,
 		                          i,
 		                          i,
 		                          getCompressedFormat(cdata->getFormat()),
 		                          getCompressedFormat(cdata->getFormat()),
@@ -162,6 +177,10 @@ void Image::uploadCompressedMipmaps()
 		                          GLsizei(cdata->getSize(i)),
 		                          GLsizei(cdata->getSize(i)),
 		                          cdata->getData(i));
 		                          cdata->getData(i));
 	}
 	}
+
+	size_t prevmemsize = textureMemorySize;
+	textureMemorySize = totalsize;
+	gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
 }
 }
 
 
 void Image::createMipmaps()
 void Image::createMipmaps()
@@ -216,6 +235,10 @@ void Image::createMipmaps()
 		                data->getData());
 		                data->getData());
 		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
 		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
 	}
 	}
+
+	size_t prevmemsize = textureMemorySize;
+	textureMemorySize = paddedWidth * paddedHeight * 4 * 1.3333;
+	gl.updateTextureMemorySize(prevmemsize, textureMemorySize);
 }
 }
 
 
 void Image::checkMipmapsCreated()
 void Image::checkMipmapsCreated()
@@ -381,6 +404,13 @@ bool Image::loadVolatile()
 	if (glerr != GL_NO_ERROR)
 	if (glerr != GL_NO_ERROR)
 		throw love::Exception("Cannot create image (error code 0x%x)", glerr);
 		throw love::Exception("Cannot create image (error code 0x%x)", glerr);
 
 
+	if (isCompressed())
+		textureMemorySize = cdata->getSize(0);
+	else
+		textureMemorySize = paddedWidth * paddedHeight * 4;
+
+	gl.updateTextureMemorySize(0, textureMemorySize);
+
 	usingDefaultTexture = false;
 	usingDefaultTexture = false;
 	mipmapsCreated = false;
 	mipmapsCreated = false;
 	checkMipmapsCreated();
 	checkMipmapsCreated();
@@ -456,6 +486,9 @@ void Image::unloadVolatile()
 	{
 	{
 		gl.deleteTexture(texture);
 		gl.deleteTexture(texture);
 		texture = 0;
 		texture = 0;
+
+		gl.updateTextureMemorySize(textureMemorySize, 0);
+		textureMemorySize = 0;
 	}
 	}
 }
 }
 
 
@@ -530,7 +563,7 @@ void Image::drawv(const Matrix &t, const Vertex *v)
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *)&v[0].s);
 
 
 	gl.prepareDraw();
 	gl.prepareDraw();
-	glDrawArrays(GL_QUADS, 0, 4);
+	gl.drawArrays(GL_QUADS, 0, 4);
 
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);

+ 4 - 0
src/modules/graphics/opengl/Image.h

@@ -148,6 +148,8 @@ public:
 	static bool getConstant(const char *in, Format &out);
 	static bool getConstant(const char *in, Format &out);
 	static bool getConstant(Format in, const char *&out);
 	static bool getConstant(Format in, const char *&out);
 
 
+	static int imageCount;
+
 private:
 private:
 
 
 	void uploadDefaultTexture();
 	void uploadDefaultTexture();
@@ -184,6 +186,8 @@ private:
 	// back to a default texture.
 	// back to a default texture.
 	bool usingDefaultTexture;
 	bool usingDefaultTexture;
 
 
+	size_t textureMemorySize;
+
 	void preload();
 	void preload();
 
 
 	void uploadTexturePadded();
 	void uploadTexturePadded();

+ 2 - 2
src/modules/graphics/opengl/Mesh.cpp

@@ -375,7 +375,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 		GLenum type = element_data_type;
 		GLenum type = element_data_type;
 		const void *indices = ibo->getPointer(min * getGLDataTypeSize(type));
 		const void *indices = ibo->getPointer(min * getGLDataTypeSize(type));
 
 
-		glDrawElements(mode, max - min + 1, type, indices);
+		gl.drawElements(mode, max - min + 1, type, indices);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -388,7 +388,7 @@ void Mesh::draw(float x, float y, float angle, float sx, float sy, float ox, flo
 			min = std::min(range_min, max);
 			min = std::min(range_min, max);
 
 
 		// Normal non-indexed drawing (no custom vertex map.)
 		// Normal non-indexed drawing (no custom vertex map.)
-		glDrawArrays(mode, min, max - min + 1);
+		gl.drawArrays(mode, min, max - min + 1);
 	}
 	}
 
 
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);

+ 24 - 1
src/modules/graphics/opengl/OpenGL.cpp

@@ -41,7 +41,8 @@ namespace opengl
 {
 {
 
 
 OpenGL::OpenGL()
 OpenGL::OpenGL()
-	: contextInitialized(false)
+	: stats()
+	, contextInitialized(false)
 	, maxAnisotropy(1.0f)
 	, maxAnisotropy(1.0f)
 	, maxTextureSize(0)
 	, maxTextureSize(0)
 	, maxRenderTargets(0)
 	, maxRenderTargets(0)
@@ -319,6 +320,18 @@ void OpenGL::prepareDraw()
 	}
 	}
 }
 }
 
 
+void OpenGL::drawArrays(GLenum mode, GLint first, GLsizei count)
+{
+	glDrawArrays(mode, first, count);
+	++stats.drawCalls;
+}
+
+void OpenGL::drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices)
+{
+	glDrawElements(mode, count, type, indices);
+	++stats.drawCalls;
+}
+
 void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
 void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount)
 {
 {
 	Shader *shader = Shader::current;
 	Shader *shader = Shader::current;
@@ -341,6 +354,8 @@ void OpenGL::drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsize
 		if (shaderHasID)
 		if (shaderHasID)
 			state.lastPseudoInstanceID = primcount - 1;
 			state.lastPseudoInstanceID = primcount - 1;
 	}
 	}
+
+	++stats.drawCalls;
 }
 }
 
 
 void OpenGL::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount)
 void OpenGL::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount)
@@ -365,6 +380,8 @@ void OpenGL::drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, cons
 		if (shaderHasID)
 		if (shaderHasID)
 			state.lastPseudoInstanceID = primcount - 1;
 			state.lastPseudoInstanceID = primcount - 1;
 	}
 	}
+
+	++stats.drawCalls;
 }
 }
 
 
 void OpenGL::setColor(const Color &c)
 void OpenGL::setColor(const Color &c)
@@ -689,6 +706,12 @@ int OpenGL::getMaxRenderTargets() const
 	return maxRenderTargets;
 	return maxRenderTargets;
 }
 }
 
 
+void OpenGL::updateTextureMemorySize(size_t oldsize, size_t newsize)
+{
+	int64 memsize = (int64) stats.textureMemory + ((int64 )newsize -  (int64) oldsize);
+	stats.textureMemory = (size_t) std::max(memsize, (int64) 0);
+}
+
 OpenGL::Vendor OpenGL::getVendor() const
 OpenGL::Vendor OpenGL::getVendor() const
 {
 {
 	return vendor;
 	return vendor;

+ 15 - 3
src/modules/graphics/opengl/OpenGL.h

@@ -133,6 +133,12 @@ public:
 		OpenGL &gl;
 		OpenGL &gl;
 	};
 	};
 
 
+	struct Stats
+	{
+		size_t textureMemory;
+		int    drawCalls;
+	} stats;
+
 	OpenGL();
 	OpenGL();
 
 
 	/**
 	/**
@@ -159,13 +165,17 @@ public:
 	void prepareDraw();
 	void prepareDraw();
 
 
 	/**
 	/**
-	 * glDrawArraysInstanced with a pseudo-instancing fallback.
+	 * glDrawArrays and glDrawElements which increment the draw-call counter by
+	 * themselves.
 	 **/
 	 **/
-	void drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount);
+	void drawArrays(GLenum mode, GLint first, GLsizei count);
+	void drawElements(GLenum mode, GLsizei count, GLenum type, const void *indices);
 
 
 	/**
 	/**
-	 * glDrawElementsInstanced with a pseudo-instancing fallback.
+	 * glDrawArraysInstanced and glDrawElementsInstanced with pseudo-instancing
+	 * fallbacks. They also increment the draw-call counter (once per call).
 	 **/
 	 **/
+	void drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount);
 	void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
 	void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount);
 
 
 	/**
 	/**
@@ -279,6 +289,8 @@ public:
 	 **/
 	 **/
 	int getMaxRenderTargets() const;
 	int getMaxRenderTargets() const;
 
 
+	void updateTextureMemorySize(size_t oldsize, size_t newsize);
+
 	/**
 	/**
 	 * Get the GPU vendor of this OpenGL context.
 	 * Get the GPU vendor of this OpenGL context.
 	 **/
 	 **/

+ 1 - 1
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -878,7 +878,7 @@ void ParticleSystem::draw(float x, float y, float angle, float sx, float sy, flo
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].s);
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (GLvoid *) &particleVerts[0].s);
 
 
 	gl.prepareDraw();
 	gl.prepareDraw();
-	glDrawArrays(GL_QUADS, 0, pCount * 4);
+	gl.drawArrays(GL_QUADS, 0, pCount * 4);
 
 
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);

+ 2 - 2
src/modules/graphics/opengl/Polyline.cpp

@@ -333,7 +333,7 @@ void Polyline::draw()
 	gl.bindTexture(0);
 	gl.bindTexture(0);
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glEnableClientState(GL_VERTEX_ARRAY);
 	glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)vertices);
 	glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)vertices);
-	glDrawArrays(draw_mode, 0, vertex_count);
+	gl.drawArrays(draw_mode, 0, vertex_count);
 
 
 	if (overdraw)
 	if (overdraw)
 	{
 	{
@@ -345,7 +345,7 @@ void Polyline::draw()
 		glEnableClientState(GL_COLOR_ARRAY);
 		glEnableClientState(GL_COLOR_ARRAY);
 		glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
 		glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)overdraw);
 		glVertexPointer(2, GL_FLOAT, 0, (const GLvoid *)overdraw);
-		glDrawArrays(draw_mode, 0, overdraw_vertex_count);
+		gl.drawArrays(draw_mode, 0, overdraw_vertex_count);
 		glDisableClientState(GL_COLOR_ARRAY);
 		glDisableClientState(GL_COLOR_ARRAY);
 
 
 		delete[] colors;
 		delete[] colors;

+ 1 - 1
src/modules/graphics/opengl/SpriteBatch.cpp

@@ -294,7 +294,7 @@ void SpriteBatch::draw(float x, float y, float angle, float sx, float sy, float
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
 	glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), array_buf->getPointer(texel_offset));
 
 
 	gl.prepareDraw();
 	gl.prepareDraw();
-	glDrawElements(GL_TRIANGLES, element_buf->getIndexCount(next), element_buf->getType(), element_buf->getPointer(0));
+	gl.drawElements(GL_TRIANGLES, element_buf->getIndexCount(next), element_buf->getType(), element_buf->getPointer(0));
 
 
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_VERTEX_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

+ 36 - 0
src/modules/graphics/opengl/wrap_Graphics.cpp

@@ -1054,6 +1054,41 @@ int w_getRendererInfo(lua_State *L)
 	return 4;
 	return 4;
 }
 }
 
 
+int w_getStats(lua_State *L)
+{
+	Graphics::Stats stats = instance()->getStats();
+
+	lua_createtable(L, 0, (int) Graphics::STAT_MAX_ENUM);
+
+	const char *sname = nullptr;
+
+	Graphics::getConstant(Graphics::STAT_DRAW_CALLS, sname);
+	lua_pushinteger(L, stats.drawCalls);
+	lua_setfield(L, -2, sname);
+
+	Graphics::getConstant(Graphics::STAT_CANVAS_SWITCHES, sname);
+	lua_pushinteger(L, stats.canvasSwitches);
+	lua_setfield(L, -2, sname);
+
+	Graphics::getConstant(Graphics::STAT_CANVASES, sname);
+	lua_pushinteger(L, stats.canvases);
+	lua_setfield(L, -2, sname);
+
+	Graphics::getConstant(Graphics::STAT_IMAGES, sname);
+	lua_pushinteger(L, stats.images);
+	lua_setfield(L, -2, sname);
+
+	Graphics::getConstant(Graphics::STAT_FONTS, sname);
+	lua_pushinteger(L, stats.fonts);
+	lua_setfield(L, -2, sname);
+
+	Graphics::getConstant(Graphics::STAT_TEXTURE_MEMORY, sname);
+	lua_pushnumber(L, (lua_Number) stats.textureMemory);
+	lua_setfield(L, -2, sname);
+
+	return 1;
+}
+
 int w_getSystemLimit(lua_State *L)
 int w_getSystemLimit(lua_State *L)
 {
 {
 	const char *limitstr = luaL_checkstring(L, 1);
 	const char *limitstr = luaL_checkstring(L, 1);
@@ -1425,6 +1460,7 @@ static const luaL_Reg functions[] =
 	{ "getCanvasFormats", w_getCanvasFormats },
 	{ "getCanvasFormats", w_getCanvasFormats },
 	{ "getCompressedImageFormats", w_getCompressedImageFormats },
 	{ "getCompressedImageFormats", w_getCompressedImageFormats },
 	{ "getRendererInfo", w_getRendererInfo },
 	{ "getRendererInfo", w_getRendererInfo },
+	{ "getStats", w_getStats },
 	{ "getSystemLimit", w_getSystemLimit },
 	{ "getSystemLimit", w_getSystemLimit },
 
 
 	{ "draw", w_draw },
 	{ "draw", w_draw },

+ 1 - 0
src/modules/graphics/opengl/wrap_Graphics.h

@@ -96,6 +96,7 @@ int w_isSupported(lua_State *L);
 int w_getCanvasFormats(lua_State *L);
 int w_getCanvasFormats(lua_State *L);
 int w_getCompressedImageFormats(lua_State *L);
 int w_getCompressedImageFormats(lua_State *L);
 int w_getRendererInfo(lua_State *L);
 int w_getRendererInfo(lua_State *L);
+int w_getStats(lua_State *L);
 int w_getSystemLimit(lua_State *L);
 int w_getSystemLimit(lua_State *L);
 int w_draw(lua_State *L);
 int w_draw(lua_State *L);
 int w_print(lua_State *L);
 int w_print(lua_State *L);