Browse Source

Begin unification of Canvases and Images.

Alex Szpakowski 5 years ago
parent
commit
63c5df6aa7

+ 1 - 25
src/modules/graphics/Canvas.cpp

@@ -102,7 +102,7 @@ Canvas::~Canvas()
 {
 {
 }
 }
 
 
-Canvas::MipmapMode Canvas::getMipmapMode() const
+Texture::MipmapsMode Canvas::getMipmapsMode() const
 {
 {
 	return settings.mipmaps;
 	return settings.mipmaps;
 }
 }
@@ -141,21 +141,6 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
 	return module->newImageData(r.w, r.h, dataformat);
 	return module->newImageData(r.w, r.h, dataformat);
 }
 }
 
 
-bool Canvas::getConstant(const char *in, MipmapMode &out)
-{
-	return mipmapModes.find(in, out);
-}
-
-bool Canvas::getConstant(MipmapMode in, const char *&out)
-{
-	return mipmapModes.find(in, out);
-}
-
-std::vector<std::string> Canvas::getConstants(MipmapMode)
-{
-	return mipmapModes.getNames();
-}
-
 bool Canvas::getConstant(const char *in, SettingType &out)
 bool Canvas::getConstant(const char *in, SettingType &out)
 {
 {
 	return settingTypes.find(in, out);
 	return settingTypes.find(in, out);
@@ -178,15 +163,6 @@ std::vector<std::string> Canvas::getConstants(SettingType)
 	return settingTypes.getNames();
 	return settingTypes.getNames();
 }
 }
 
 
-StringMap<Canvas::MipmapMode, Canvas::MIPMAPS_MAX_ENUM>::Entry Canvas::mipmapEntries[] =
-{
-	{ "none",   MIPMAPS_NONE   },
-	{ "manual", MIPMAPS_MANUAL },
-	{ "auto",   MIPMAPS_AUTO   },
-};
-
-StringMap<Canvas::MipmapMode, Canvas::MIPMAPS_MAX_ENUM> Canvas::mipmapModes(Canvas::mipmapEntries, sizeof(Canvas::mipmapEntries));
-
 StringMap<Canvas::SettingType, Canvas::SETTING_MAX_ENUM>::Entry Canvas::settingTypeEntries[] =
 StringMap<Canvas::SettingType, Canvas::SETTING_MAX_ENUM>::Entry Canvas::settingTypeEntries[] =
 {
 {
 	// Width / height / layers are currently omittted because they're separate
 	// Width / height / layers are currently omittted because they're separate

+ 4 - 16
src/modules/graphics/Canvas.h

@@ -39,14 +39,6 @@ public:
 
 
 	static love::Type type;
 	static love::Type type;
 
 
-	enum MipmapMode
-	{
-		MIPMAPS_NONE,
-		MIPMAPS_MANUAL,
-		MIPMAPS_AUTO,
-		MIPMAPS_MAX_ENUM
-	};
-
 	enum SettingType
 	enum SettingType
 	{
 	{
 		SETTING_WIDTH,
 		SETTING_WIDTH,
@@ -66,7 +58,7 @@ public:
 		int width  = 1;
 		int width  = 1;
 		int height = 1;
 		int height = 1;
 		int layers = 1; // depth for 3D textures
 		int layers = 1; // depth for 3D textures
-		MipmapMode mipmaps = MIPMAPS_NONE;
+		MipmapsMode mipmaps = MIPMAPS_NONE;
 		PixelFormat format = PIXELFORMAT_NORMAL;
 		PixelFormat format = PIXELFORMAT_NORMAL;
 		TextureType type = TEXTURE_2D;
 		TextureType type = TEXTURE_2D;
 		float dpiScale = 1.0f;
 		float dpiScale = 1.0f;
@@ -77,14 +69,10 @@ public:
 	Canvas(const Settings &settings);
 	Canvas(const Settings &settings);
 	virtual ~Canvas();
 	virtual ~Canvas();
 
 
-	MipmapMode getMipmapMode() const;
+	MipmapsMode getMipmapsMode() const;
 
 
 	virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
 	virtual love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect);
 
 
-	static bool getConstant(const char *in, MipmapMode &out);
-	static bool getConstant(MipmapMode in, const char *&out);
-	static std::vector<std::string> getConstants(MipmapMode);
-
 	static bool getConstant(const char *in, SettingType &out);
 	static bool getConstant(const char *in, SettingType &out);
 	static bool getConstant(SettingType in, const char *&out);
 	static bool getConstant(SettingType in, const char *&out);
 	static const char *getConstant(SettingType in);
 	static const char *getConstant(SettingType in);
@@ -96,8 +84,8 @@ protected:
 
 
 private:
 private:
 
 
-	static StringMap<MipmapMode, MIPMAPS_MAX_ENUM>::Entry mipmapEntries[];
-	static StringMap<MipmapMode, MIPMAPS_MAX_ENUM> mipmapModes;
+	static StringMap<MipmapsMode, MIPMAPS_MAX_ENUM>::Entry mipmapEntries[];
+	static StringMap<MipmapsMode, MIPMAPS_MAX_ENUM> mipmapModes;
 
 
 	static StringMap<SettingType, SETTING_MAX_ENUM>::Entry settingTypeEntries[];
 	static StringMap<SettingType, SETTING_MAX_ENUM>::Entry settingTypeEntries[];
 	static StringMap<SettingType, SETTING_MAX_ENUM> settingTypes;
 	static StringMap<SettingType, SETTING_MAX_ENUM> settingTypes;

+ 56 - 47
src/modules/graphics/Graphics.cpp

@@ -116,7 +116,7 @@ Graphics::Graphics()
 	, writingToStencil(false)
 	, writingToStencil(false)
 	, streamBufferState()
 	, streamBufferState()
 	, projectionMatrix()
 	, projectionMatrix()
-	, canvasSwitchCount(0)
+	, renderTargetSwitchCount(0)
 	, drawCalls(0)
 	, drawCalls(0)
 	, drawCallsBatched(0)
 	, drawCallsBatched(0)
 	, quadIndexBuffer(nullptr)
 	, quadIndexBuffer(nullptr)
@@ -335,8 +335,8 @@ int Graphics::getPixelHeight() const
 double Graphics::getCurrentDPIScale() const
 double Graphics::getCurrentDPIScale() const
 {
 {
 	const auto &rt = states.back().renderTargets.getFirstTarget();
 	const auto &rt = states.back().renderTargets.getFirstTarget();
-	if (rt.canvas.get())
-		return rt.canvas->getDPIScale();
+	if (rt.texture.get())
+		return rt.texture->getDPIScale();
 
 
 	return getScreenDPIScale();
 	return getScreenDPIScale();
 }
 }
@@ -545,7 +545,7 @@ love::graphics::Shader *Graphics::getShader() const
 
 
 void Graphics::setCanvas(RenderTarget rt, uint32 temporaryRTFlags)
 void Graphics::setCanvas(RenderTarget rt, uint32 temporaryRTFlags)
 {
 {
-	if (rt.canvas == nullptr)
+	if (rt.texture == nullptr)
 		return setCanvas();
 		return setCanvas();
 
 
 	RenderTargets rts;
 	RenderTargets rts;
@@ -561,9 +561,9 @@ void Graphics::setCanvas(const RenderTargetsStrongRef &rts)
 	targets.colors.reserve(rts.colors.size());
 	targets.colors.reserve(rts.colors.size());
 
 
 	for (const auto &rt : rts.colors)
 	for (const auto &rt : rts.colors)
-		targets.colors.emplace_back(rt.canvas.get(), rt.slice, rt.mipmap);
+		targets.colors.emplace_back(rt.texture.get(), rt.slice, rt.mipmap);
 
 
-	targets.depthStencil = RenderTarget(rts.depthStencil.canvas, rts.depthStencil.slice, rts.depthStencil.mipmap);
+	targets.depthStencil = RenderTarget(rts.depthStencil.texture, rts.depthStencil.slice, rts.depthStencil.mipmap);
 	targets.temporaryRTFlags = rts.temporaryRTFlags;
 	targets.temporaryRTFlags = rts.temporaryRTFlags;
 
 
 	return setCanvas(targets);
 	return setCanvas(targets);
@@ -575,9 +575,9 @@ void Graphics::setCanvas(const RenderTargets &rts)
 	int ncanvases = (int) rts.colors.size();
 	int ncanvases = (int) rts.colors.size();
 
 
 	RenderTarget firsttarget = rts.getFirstTarget();
 	RenderTarget firsttarget = rts.getFirstTarget();
-	love::graphics::Canvas *firstcanvas = firsttarget.canvas;
+	Texture *firsttex = firsttarget.texture;
 
 
-	if (firstcanvas == nullptr)
+	if (firsttex == nullptr)
 		return setCanvas();
 		return setCanvas();
 
 
 	const auto &prevRTs = state.renderTargets;
 	const auto &prevRTs = state.renderTargets;
@@ -612,29 +612,35 @@ void Graphics::setCanvas(const RenderTargets &rts)
 
 
 	PixelFormat firstcolorformat = PIXELFORMAT_UNKNOWN;
 	PixelFormat firstcolorformat = PIXELFORMAT_UNKNOWN;
 	if (!rts.colors.empty())
 	if (!rts.colors.empty())
-		firstcolorformat = rts.colors[0].canvas->getPixelFormat();
+		firstcolorformat = rts.colors[0].texture->getPixelFormat();
+
+	if (!firsttex->isRenderTarget())
+		throw love::Exception("Texture must be created as a render target to be used in setCanvas.");
 
 
 	if (isPixelFormatDepthStencil(firstcolorformat))
 	if (isPixelFormatDepthStencil(firstcolorformat))
 		throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
 		throw love::Exception("Depth/stencil format Canvases must be used with the 'depthstencil' field of the table passed into setCanvas.");
 
 
-	if (firsttarget.mipmap < 0 || firsttarget.mipmap >= firstcanvas->getMipmapCount())
+	if (firsttarget.mipmap < 0 || firsttarget.mipmap >= firsttex->getMipmapCount())
 		throw love::Exception("Invalid mipmap level %d.", firsttarget.mipmap + 1);
 		throw love::Exception("Invalid mipmap level %d.", firsttarget.mipmap + 1);
 
 
-	if (!firstcanvas->isValidSlice(firsttarget.slice))
+	if (!firsttex->isValidSlice(firsttarget.slice))
 		throw love::Exception("Invalid slice index: %d.", firsttarget.slice + 1);
 		throw love::Exception("Invalid slice index: %d.", firsttarget.slice + 1);
 
 
 	bool hasSRGBcanvas = firstcolorformat == PIXELFORMAT_sRGBA8_UNORM;
 	bool hasSRGBcanvas = firstcolorformat == PIXELFORMAT_sRGBA8_UNORM;
-	int pixelw = firstcanvas->getPixelWidth(firsttarget.mipmap);
-	int pixelh = firstcanvas->getPixelHeight(firsttarget.mipmap);
-	int reqmsaa = firstcanvas->getRequestedMSAA();
+	int pixelw = firsttex->getPixelWidth(firsttarget.mipmap);
+	int pixelh = firsttex->getPixelHeight(firsttarget.mipmap);
+	int reqmsaa = firsttex->getRequestedMSAA();
 
 
 	for (int i = 1; i < ncanvases; i++)
 	for (int i = 1; i < ncanvases; i++)
 	{
 	{
-		love::graphics::Canvas *c = rts.colors[i].canvas;
+		Texture *c = rts.colors[i].texture;
 		PixelFormat format = c->getPixelFormat();
 		PixelFormat format = c->getPixelFormat();
 		int mip = rts.colors[i].mipmap;
 		int mip = rts.colors[i].mipmap;
 		int slice = rts.colors[i].slice;
 		int slice = rts.colors[i].slice;
 
 
+		if (!c->isRenderTarget())
+			throw love::Exception("Texture must be created as a render target to be used in setCanvas.");
+
 		if (mip < 0 || mip >= c->getMipmapCount())
 		if (mip < 0 || mip >= c->getMipmapCount())
 			throw love::Exception("Invalid mipmap level %d.", mip + 1);
 			throw love::Exception("Invalid mipmap level %d.", mip + 1);
 
 
@@ -657,20 +663,23 @@ void Graphics::setCanvas(const RenderTargets &rts)
 			hasSRGBcanvas = true;
 			hasSRGBcanvas = true;
 	}
 	}
 
 
-	if (rts.depthStencil.canvas != nullptr)
+	if (rts.depthStencil.texture != nullptr)
 	{
 	{
-		love::graphics::Canvas *c = rts.depthStencil.canvas;
+		Texture *c = rts.depthStencil.texture;
 		int mip = rts.depthStencil.mipmap;
 		int mip = rts.depthStencil.mipmap;
 		int slice = rts.depthStencil.slice;
 		int slice = rts.depthStencil.slice;
 
 
+		if (!c->isRenderTarget())
+			throw love::Exception("Texture must be created as a render target to be used in setCanvas.");
+
 		if (!isPixelFormatDepthStencil(c->getPixelFormat()))
 		if (!isPixelFormatDepthStencil(c->getPixelFormat()))
-			throw love::Exception("Only depth/stencil format Canvases can be used with the 'depthstencil' field of the table passed into setCanvas.");
+			throw love::Exception("Only depth/stencil format Texture can be used with the 'depthstencil' field of the table passed into setCanvas.");
 
 
 		if (c->getPixelWidth(mip) != pixelw || c->getPixelHeight(mip) != pixelh)
 		if (c->getPixelWidth(mip) != pixelw || c->getPixelHeight(mip) != pixelh)
-			throw love::Exception("All canvases must have the same pixel dimensions.");
+			throw love::Exception("All Textures must have the same pixel dimensions.");
 
 
-		if (c->getRequestedMSAA() != firstcanvas->getRequestedMSAA())
-			throw love::Exception("All Canvases must have the same MSAA value.");
+		if (c->getRequestedMSAA() != firsttex->getRequestedMSAA())
+			throw love::Exception("All Textures must have the same MSAA value.");
 
 
 		if (mip < 0 || mip >= c->getMipmapCount())
 		if (mip < 0 || mip >= c->getMipmapCount())
 			throw love::Exception("Invalid mipmap level %d.", mip + 1);
 			throw love::Exception("Invalid mipmap level %d.", mip + 1);
@@ -679,12 +688,12 @@ void Graphics::setCanvas(const RenderTargets &rts)
 			throw love::Exception("Invalid slice index: %d.", slice + 1);
 			throw love::Exception("Invalid slice index: %d.", slice + 1);
 	}
 	}
 
 
-	int w = firstcanvas->getWidth(firsttarget.mipmap);
-	int h = firstcanvas->getHeight(firsttarget.mipmap);
+	int w = firsttex->getWidth(firsttarget.mipmap);
+	int h = firsttex->getHeight(firsttarget.mipmap);
 
 
 	flushStreamDraws();
 	flushStreamDraws();
 
 
-	if (rts.depthStencil.canvas == nullptr && rts.temporaryRTFlags != 0)
+	if (rts.depthStencil.texture == nullptr && rts.temporaryRTFlags != 0)
 	{
 	{
 		bool wantsdepth   = (rts.temporaryRTFlags & TEMPORARY_RT_DEPTH) != 0;
 		bool wantsdepth   = (rts.temporaryRTFlags & TEMPORARY_RT_DEPTH) != 0;
 		bool wantsstencil = (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) != 0;
 		bool wantsstencil = (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) != 0;
@@ -703,7 +712,7 @@ void Graphics::setCanvas(const RenderTargets &rts)
 		// we don't want to directly store it in the main graphics state.
 		// we don't want to directly store it in the main graphics state.
 		RenderTargets realRTs = rts;
 		RenderTargets realRTs = rts;
 
 
-		realRTs.depthStencil.canvas = getTemporaryCanvas(dsformat, pixelw, pixelh, reqmsaa);
+		realRTs.depthStencil.texture = getTemporaryTexture(dsformat, pixelw, pixelh, reqmsaa);
 		realRTs.depthStencil.slice = 0;
 		realRTs.depthStencil.slice = 0;
 
 
 		setCanvasInternal(realRTs, w, h, pixelw, pixelh, hasSRGBcanvas);
 		setCanvasInternal(realRTs, w, h, pixelw, pixelh, hasSRGBcanvas);
@@ -715,28 +724,28 @@ void Graphics::setCanvas(const RenderTargets &rts)
 	refs.colors.reserve(rts.colors.size());
 	refs.colors.reserve(rts.colors.size());
 
 
 	for (auto c : rts.colors)
 	for (auto c : rts.colors)
-		refs.colors.emplace_back(c.canvas, c.slice, c.mipmap);
+		refs.colors.emplace_back(c.texture, c.slice, c.mipmap);
 
 
-	refs.depthStencil = RenderTargetStrongRef(rts.depthStencil.canvas, rts.depthStencil.slice);
+	refs.depthStencil = RenderTargetStrongRef(rts.depthStencil.texture, rts.depthStencil.slice);
 	refs.temporaryRTFlags = rts.temporaryRTFlags;
 	refs.temporaryRTFlags = rts.temporaryRTFlags;
 
 
 	std::swap(state.renderTargets, refs);
 	std::swap(state.renderTargets, refs);
 
 
-	canvasSwitchCount++;
+	renderTargetSwitchCount++;
 }
 }
 
 
 void Graphics::setCanvas()
 void Graphics::setCanvas()
 {
 {
 	DisplayState &state = states.back();
 	DisplayState &state = states.back();
 
 
-	if (state.renderTargets.colors.empty() && state.renderTargets.depthStencil.canvas == nullptr)
+	if (state.renderTargets.colors.empty() && state.renderTargets.depthStencil.texture == nullptr)
 		return;
 		return;
 
 
 	flushStreamDraws();
 	flushStreamDraws();
 	setCanvasInternal(RenderTargets(), width, height, pixelWidth, pixelHeight, isGammaCorrect());
 	setCanvasInternal(RenderTargets(), width, height, pixelWidth, pixelHeight, isGammaCorrect());
 
 
 	state.renderTargets = RenderTargetsStrongRef();
 	state.renderTargets = RenderTargetsStrongRef();
-	canvasSwitchCount++;
+	renderTargetSwitchCount++;
 }
 }
 
 
 Graphics::RenderTargets Graphics::getCanvas() const
 Graphics::RenderTargets Graphics::getCanvas() const
@@ -747,9 +756,9 @@ Graphics::RenderTargets Graphics::getCanvas() const
 	rts.colors.reserve(curRTs.colors.size());
 	rts.colors.reserve(curRTs.colors.size());
 
 
 	for (const auto &rt : curRTs.colors)
 	for (const auto &rt : curRTs.colors)
-		rts.colors.emplace_back(rt.canvas.get(), rt.slice, rt.mipmap);
+		rts.colors.emplace_back(rt.texture.get(), rt.slice, rt.mipmap);
 
 
-	rts.depthStencil = RenderTarget(curRTs.depthStencil.canvas, curRTs.depthStencil.slice, curRTs.depthStencil.mipmap);
+	rts.depthStencil = RenderTarget(curRTs.depthStencil.texture, curRTs.depthStencil.slice, curRTs.depthStencil.mipmap);
 	rts.temporaryRTFlags = curRTs.temporaryRTFlags;
 	rts.temporaryRTFlags = curRTs.temporaryRTFlags;
 
 
 	return rts;
 	return rts;
@@ -758,7 +767,7 @@ Graphics::RenderTargets Graphics::getCanvas() const
 bool Graphics::isCanvasActive() const
 bool Graphics::isCanvasActive() const
 {
 {
 	const auto &rts = states.back().renderTargets;
 	const auto &rts = states.back().renderTargets;
-	return !rts.colors.empty() || rts.depthStencil.canvas != nullptr;
+	return !rts.colors.empty() || rts.depthStencil.texture != nullptr;
 }
 }
 
 
 bool Graphics::isRenderTargetActive(Texture *texture) const
 bool Graphics::isRenderTargetActive(Texture *texture) const
@@ -767,11 +776,11 @@ bool Graphics::isRenderTargetActive(Texture *texture) const
 
 
 	for (const auto &rt : rts.colors)
 	for (const auto &rt : rts.colors)
 	{
 	{
-		if (rt.canvas.get() == texture)
+		if (rt.texture.get() == texture)
 			return true;
 			return true;
 	}
 	}
 
 
-	if (rts.depthStencil.canvas.get() == texture)
+	if (rts.depthStencil.texture.get() == texture)
 		return true;
 		return true;
 
 
 	return false;
 	return false;
@@ -783,33 +792,33 @@ bool Graphics::isRenderTargetActive(Texture *texture, int slice) const
 
 
 	for (const auto &rt : rts.colors)
 	for (const auto &rt : rts.colors)
 	{
 	{
-		if (rt.canvas.get() == texture && rt.slice == slice)
+		if (rt.texture.get() == texture && rt.slice == slice)
 			return true;
 			return true;
 	}
 	}
 
 
-	if (rts.depthStencil.canvas.get() == texture && rts.depthStencil.slice == slice)
+	if (rts.depthStencil.texture.get() == texture && rts.depthStencil.slice == slice)
 		return true;
 		return true;
 
 
 	return false;
 	return false;
 }
 }
 
 
-Canvas *Graphics::getTemporaryCanvas(PixelFormat format, int w, int h, int samples)
+Texture *Graphics::getTemporaryTexture(PixelFormat format, int w, int h, int samples)
 {
 {
-	love::graphics::Canvas *canvas = nullptr;
+	Texture *texture = nullptr;
 
 
-	for (TemporaryCanvas &temp : temporaryCanvases)
+	for (TemporaryTexture &temp : temporaryTextures)
 	{
 	{
-		Canvas *c = temp.canvas;
+		Texture *c = temp.texture;
 		if (c->getPixelFormat() == format && c->getPixelWidth() == w
 		if (c->getPixelFormat() == format && c->getPixelWidth() == w
 			&& c->getPixelHeight() == h && c->getRequestedMSAA() == samples)
 			&& c->getPixelHeight() == h && c->getRequestedMSAA() == samples)
 		{
 		{
-			canvas = c;
+			texture = c;
 			temp.framesSinceUse = 0;
 			temp.framesSinceUse = 0;
 			break;
 			break;
 		}
 		}
 	}
 	}
 
 
-	if (canvas == nullptr)
+	if (texture == nullptr)
 	{
 	{
 		Canvas::Settings settings;
 		Canvas::Settings settings;
 		settings.format = format;
 		settings.format = format;
@@ -817,12 +826,12 @@ Canvas *Graphics::getTemporaryCanvas(PixelFormat format, int w, int h, int sampl
 		settings.height = h;
 		settings.height = h;
 		settings.msaa = samples;
 		settings.msaa = samples;
 
 
-		canvas = newCanvas(settings);
+		texture = newCanvas(settings);
 
 
-		temporaryCanvases.emplace_back(canvas);
+		temporaryTextures.emplace_back(texture);
 	}
 	}
 
 
-	return canvas;
+	return texture;
 }
 }
 
 
 void Graphics::intersectScissor(const Rect &rect)
 void Graphics::intersectScissor(const Rect &rect)
@@ -1591,7 +1600,7 @@ Graphics::Stats Graphics::getStats() const
 	if (streamBufferState.vertexCount > 0)
 	if (streamBufferState.vertexCount > 0)
 		stats.drawCalls++;
 		stats.drawCalls++;
 
 
-	stats.canvasSwitches = canvasSwitchCount;
+	stats.renderTargetSwitches = renderTargetSwitchCount;
 	stats.drawCallsBatched = drawCallsBatched;
 	stats.drawCallsBatched = drawCallsBatched;
 	stats.textures = Texture::textureCount;
 	stats.textures = Texture::textureCount;
 	stats.fonts = Font::fontCount;
 	stats.fonts = Font::fontCount;

+ 22 - 22
src/modules/graphics/Graphics.h

@@ -200,7 +200,7 @@ public:
 	{
 	{
 		int drawCalls;
 		int drawCalls;
 		int drawCallsBatched;
 		int drawCallsBatched;
-		int canvasSwitches;
+		int renderTargetSwitches;
 		int shaderSwitches;
 		int shaderSwitches;
 		int textures;
 		int textures;
 		int fonts;
 		int fonts;
@@ -315,53 +315,53 @@ public:
 
 
 	struct RenderTarget
 	struct RenderTarget
 	{
 	{
-		Canvas *canvas;
+		Texture *texture;
 		int slice;
 		int slice;
 		int mipmap;
 		int mipmap;
 
 
-		RenderTarget(Canvas *canvas, int slice = 0, int mipmap = 0)
-			: canvas(canvas)
+		RenderTarget(Texture *texture, int slice = 0, int mipmap = 0)
+			: texture(texture)
 			, slice(slice)
 			, slice(slice)
 			, mipmap(mipmap)
 			, mipmap(mipmap)
 		{}
 		{}
 
 
 		RenderTarget()
 		RenderTarget()
-			: canvas(nullptr)
+			: texture(nullptr)
 			, slice(0)
 			, slice(0)
 			, mipmap(0)
 			, mipmap(0)
 		{}
 		{}
 
 
 		bool operator != (const RenderTarget &other) const
 		bool operator != (const RenderTarget &other) const
 		{
 		{
-			return canvas != other.canvas || slice != other.slice || mipmap != other.mipmap;
+			return texture != other.texture || slice != other.slice || mipmap != other.mipmap;
 		}
 		}
 
 
 		bool operator != (const RenderTargetStrongRef &other) const
 		bool operator != (const RenderTargetStrongRef &other) const
 		{
 		{
-			return canvas != other.canvas.get() || slice != other.slice || mipmap != other.mipmap;
+			return texture != other.texture.get() || slice != other.slice || mipmap != other.mipmap;
 		}
 		}
 	};
 	};
 
 
 	struct RenderTargetStrongRef
 	struct RenderTargetStrongRef
 	{
 	{
-		StrongRef<Canvas> canvas;
+		StrongRef<Texture> texture;
 		int slice = 0;
 		int slice = 0;
 		int mipmap = 0;
 		int mipmap = 0;
 
 
-		RenderTargetStrongRef(Canvas *canvas, int slice = 0, int mipmap = 0)
-			: canvas(canvas)
+		RenderTargetStrongRef(Texture *texture, int slice = 0, int mipmap = 0)
+			: texture(texture)
 			, slice(slice)
 			, slice(slice)
 			, mipmap(mipmap)
 			, mipmap(mipmap)
 		{}
 		{}
 
 
 		bool operator != (const RenderTargetStrongRef &other) const
 		bool operator != (const RenderTargetStrongRef &other) const
 		{
 		{
-			return canvas.get() != other.canvas.get() || slice != other.slice || mipmap != other.mipmap;
+			return texture.get() != other.texture.get() || slice != other.slice || mipmap != other.mipmap;
 		}
 		}
 
 
 		bool operator != (const RenderTarget &other) const
 		bool operator != (const RenderTarget &other) const
 		{
 		{
-			return canvas.get() != other.canvas || slice != other.slice || mipmap != other.mipmap;
+			return texture.get() != other.texture || slice != other.slice || mipmap != other.mipmap;
 		}
 		}
 	};
 	};
 
 
@@ -619,12 +619,12 @@ public:
 	const BlendState &getBlendState() const;
 	const BlendState &getBlendState() const;
 
 
 	/**
 	/**
-	 * Sets the default sampler state for images, canvases, and fonts.
+	 * Sets the default sampler state for textures, videos, and fonts.
 	 **/
 	 **/
 	void setDefaultSamplerState(const SamplerState &s);
 	void setDefaultSamplerState(const SamplerState &s);
 
 
 	/**
 	/**
-	 * Gets the default sampler state for images, canvases, and fonts.
+	 * Gets the default sampler state for textures, videos, and fonts.
 	 **/
 	 **/
 	const SamplerState &getDefaultSamplerState() const;
 	const SamplerState &getDefaultSamplerState() const;
 
 
@@ -939,13 +939,13 @@ protected:
 		}
 		}
 	};
 	};
 
 
-	struct TemporaryCanvas
+	struct TemporaryTexture
 	{
 	{
-		Canvas *canvas;
+		Texture *texture;
 		int framesSinceUse;
 		int framesSinceUse;
 
 
-		TemporaryCanvas(Canvas *c)
-			: canvas(c)
+		TemporaryTexture(Texture *tex)
+			: texture(tex)
 			, framesSinceUse(0)
 			, framesSinceUse(0)
 		{}
 		{}
 	};
 	};
@@ -961,7 +961,7 @@ protected:
 
 
 	void createQuadIndexBuffer();
 	void createQuadIndexBuffer();
 
 
-	Canvas *getTemporaryCanvas(PixelFormat format, int w, int h, int samples);
+	Texture *getTemporaryTexture(PixelFormat format, int w, int h, int samples);
 
 
 	void restoreState(const DisplayState &s);
 	void restoreState(const DisplayState &s);
 	void restoreStateChecked(const DisplayState &s);
 	void restoreStateChecked(const DisplayState &s);
@@ -994,9 +994,9 @@ protected:
 	std::vector<DisplayState> states;
 	std::vector<DisplayState> states;
 	std::vector<StackType> stackTypeStack;
 	std::vector<StackType> stackTypeStack;
 
 
-	std::vector<TemporaryCanvas> temporaryCanvases;
+	std::vector<TemporaryTexture> temporaryTextures;
 
 
-	int canvasSwitchCount;
+	int renderTargetSwitchCount;
 	int drawCalls;
 	int drawCalls;
 	int drawCallsBatched;
 	int drawCallsBatched;
 
 
@@ -1007,7 +1007,7 @@ protected:
 	Deprecations deprecations;
 	Deprecations deprecations;
 
 
 	static const size_t MAX_USER_STACK_DEPTH = 128;
 	static const size_t MAX_USER_STACK_DEPTH = 128;
-	static const int MAX_TEMPORARY_CANVAS_UNUSED_FRAMES = 16;
+	static const int MAX_TEMPORARY_TEXTURE_UNUSED_FRAMES = 16;
 
 
 private:
 private:
 
 

+ 16 - 20
src/modules/graphics/Image.cpp

@@ -31,20 +31,17 @@ namespace graphics
 
 
 love::Type Image::type("Image", &Texture::type);
 love::Type Image::type("Image", &Texture::type);
 
 
-Image::Image(const Slices &data, const Settings &settings, bool validatedata)
-	: Texture(data.getTextureType())
+Image::Image(TextureType textype, const Settings &settings)
+	: Texture(textype)
 	, settings(settings)
 	, settings(settings)
-	, mipmapsType(settings.mipmaps ? MIPMAPS_GENERATED : MIPMAPS_NONE)
 	, usingDefaultTexture(false)
 	, usingDefaultTexture(false)
 {
 {
 	renderTarget = false;
 	renderTarget = false;
 	sRGB = isGammaCorrect() && !settings.linear;
 	sRGB = isGammaCorrect() && !settings.linear;
-	if (validatedata && data.validate() && data.getMipmapCount() > 1)
-		mipmapsType = MIPMAPS_DATA;
 }
 }
 
 
 Image::Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings)
 Image::Image(TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings)
-	: Image(Slices(textype), settings, false)
+	: Image(textype, settings)
 {
 {
 	if (isPixelFormatCompressed(format))
 	if (isPixelFormatCompressed(format))
 		throw love::Exception("This constructor is only supported for non-compressed pixel formats.");
 		throw love::Exception("This constructor is only supported for non-compressed pixel formats.");
@@ -54,26 +51,30 @@ Image::Image(TextureType textype, PixelFormat format, int width, int height, int
 	else if (textype == TEXTURE_VOLUME)
 	else if (textype == TEXTURE_VOLUME)
 		depth = slices;
 		depth = slices;
 
 
-	init(format, width, height, settings);
+	init(format, width, height, 1, settings);
 }
 }
 
 
 Image::Image(const Slices &slices, const Settings &settings)
 Image::Image(const Slices &slices, const Settings &settings)
-	: Image(slices, settings, true)
+	: Image(slices.getTextureType(), settings)
 {
 {
+	int dataMipmaps = 1;
+	if (slices.validate() && slices.getMipmapCount() > 1)
+		dataMipmaps = slices.getMipmapCount();
+
 	if (texType == TEXTURE_2D_ARRAY)
 	if (texType == TEXTURE_2D_ARRAY)
 		this->layers = slices.getSliceCount();
 		this->layers = slices.getSliceCount();
 	else if (texType == TEXTURE_VOLUME)
 	else if (texType == TEXTURE_VOLUME)
 		this->depth = slices.getSliceCount();
 		this->depth = slices.getSliceCount();
 
 
 	love::image::ImageDataBase *slice = slices.get(0, 0);
 	love::image::ImageDataBase *slice = slices.get(0, 0);
-	init(slice->getFormat(), slice->getWidth(), slice->getHeight(), settings);
+	init(slice->getFormat(), slice->getWidth(), slice->getHeight(), dataMipmaps, settings);
 }
 }
 
 
 Image::~Image()
 Image::~Image()
 {
 {
 }
 }
 
 
-void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
+void Image::init(PixelFormat fmt, int w, int h, int dataMipmaps, const Settings &settings)
 {
 {
 	Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 	Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 	if (gfx != nullptr && !gfx->isPixelFormatSupported(fmt, renderTarget, readable, sRGB))
 	if (gfx != nullptr && !gfx->isPixelFormatSupported(fmt, renderTarget, readable, sRGB))
@@ -96,10 +97,10 @@ void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
 
 
 	format = fmt;
 	format = fmt;
 
 
-	if (isCompressed() && mipmapsType == MIPMAPS_GENERATED)
-		mipmapsType = MIPMAPS_NONE;
-
-	mipmapCount = mipmapsType == MIPMAPS_NONE ? 1 : getTotalMipmapCount(w, h, depth);
+	if (!settings.mipmaps || (isCompressed() && dataMipmaps <= 1))
+		mipmapCount = 1;
+	else
+		mipmapCount = getTotalMipmapCount(w, h, depth);
 
 
 	initQuad();
 	initQuad();
 }
 }
@@ -125,7 +126,7 @@ void Image::replacePixels(love::image::ImageDataBase *d, int slice, int mipmap,
 	if (d->getFormat() != getPixelFormat())
 	if (d->getFormat() != getPixelFormat())
 		throw love::Exception("Pixel formats must match.");
 		throw love::Exception("Pixel formats must match.");
 
 
-	if (mipmap < 0 || (mipmapsType != MIPMAPS_DATA && mipmap > 0) || mipmap >= getMipmapCount())
+	if (mipmap < 0 || mipmap >= getMipmapCount())
 		throw love::Exception("Invalid image mipmap index %d.", mipmap + 1);
 		throw love::Exception("Invalid image mipmap index %d.", mipmap + 1);
 
 
 	if (slice < 0 || (texType == TEXTURE_CUBE && slice >= 6)
 	if (slice < 0 || (texType == TEXTURE_CUBE && slice >= 6)
@@ -168,11 +169,6 @@ void Image::replacePixels(const void *data, size_t size, int slice, int mipmap,
 		generateMipmaps();
 		generateMipmaps();
 }
 }
 
 
-Image::MipmapsType Image::getMipmapsType() const
-{
-	return mipmapsType;
-}
-
 bool Image::getConstant(const char *in, SettingType &out)
 bool Image::getConstant(const char *in, SettingType &out)
 {
 {
 	return settingTypes.find(in, out);
 	return settingTypes.find(in, out);

+ 2 - 6
src/modules/graphics/Image.h

@@ -57,8 +57,6 @@ public:
 	void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, int x, int y, bool reloadmipmaps);
 	void replacePixels(love::image::ImageDataBase *d, int slice, int mipmap, int x, int y, bool reloadmipmaps);
 	void replacePixels(const void *data, size_t size, int slice, int mipmap, const Rect &rect, bool reloadmipmaps);
 	void replacePixels(const void *data, size_t size, int slice, int mipmap, const Rect &rect, bool reloadmipmaps);
 
 
-	MipmapsType getMipmapsType() const;
-
 	static bool getConstant(const char *in, SettingType &out);
 	static bool getConstant(const char *in, SettingType &out);
 	static bool getConstant(SettingType in, const char *&out);
 	static bool getConstant(SettingType in, const char *&out);
 	static const char *getConstant(SettingType in);
 	static const char *getConstant(SettingType in);
@@ -75,17 +73,15 @@ protected:
 	// The settings used to initialize this Image.
 	// The settings used to initialize this Image.
 	Settings settings;
 	Settings settings;
 
 
-	MipmapsType mipmapsType;
-
 	// True if the image wasn't able to be properly created and it had to fall
 	// True if the image wasn't able to be properly created and it had to fall
 	// back to a default texture.
 	// back to a default texture.
 	bool usingDefaultTexture;
 	bool usingDefaultTexture;
 
 
 private:
 private:
 
 
-	Image(const Slices &data, const Settings &settings, bool validatedata);
+	Image(TextureType textype, const Settings &settings);
 
 
-	void init(PixelFormat fmt, int w, int h, const Settings &settings);
+	void init(PixelFormat fmt, int w, int h, int dataMipmaps, const Settings &settings);
 
 
 	static StringMap<SettingType, SETTING_MAX_ENUM>::Entry settingTypeEntries[];
 	static StringMap<SettingType, SETTING_MAX_ENUM>::Entry settingTypeEntries[];
 	static StringMap<SettingType, SETTING_MAX_ENUM> settingTypes;
 	static StringMap<SettingType, SETTING_MAX_ENUM> settingTypes;

+ 24 - 0
src/modules/graphics/Texture.cpp

@@ -680,5 +680,29 @@ StringMap<TextureType, TEXTURE_MAX_ENUM>::Entry Texture::texTypeEntries[] =
 
 
 StringMap<TextureType, TEXTURE_MAX_ENUM> Texture::texTypes(Texture::texTypeEntries, sizeof(Texture::texTypeEntries));
 StringMap<TextureType, TEXTURE_MAX_ENUM> Texture::texTypes(Texture::texTypeEntries, sizeof(Texture::texTypeEntries));
 
 
+static StringMap<Texture::MipmapsMode, Texture::MIPMAPS_MAX_ENUM>::Entry mipmapEntries[] =
+{
+	{ "none",   Texture::MIPMAPS_NONE   },
+	{ "manual", Texture::MIPMAPS_MANUAL },
+	{ "auto",   Texture::MIPMAPS_AUTO   },
+};
+
+static StringMap<Texture::MipmapsMode, Texture::MIPMAPS_MAX_ENUM> mipmapModes(mipmapEntries, sizeof(mipmapEntries));
+
+bool Texture::getConstant(const char *in, MipmapsMode &out)
+{
+	return mipmapModes.find(in, out);
+}
+
+bool Texture::getConstant(MipmapsMode in, const char *&out)
+{
+	return mipmapModes.find(in, out);
+}
+
+std::vector<std::string> Texture::getConstants(MipmapsMode)
+{
+	return mipmapModes.getNames();
+}
+
 } // graphics
 } // graphics
 } // love
 } // love

+ 8 - 3
src/modules/graphics/Texture.h

@@ -128,11 +128,12 @@ public:
 	static love::Type type;
 	static love::Type type;
 	static int textureCount;
 	static int textureCount;
 
 
-	enum MipmapsType
+	enum MipmapsMode
 	{
 	{
 		MIPMAPS_NONE,
 		MIPMAPS_NONE,
-		MIPMAPS_DATA,
-		MIPMAPS_GENERATED,
+		MIPMAPS_MANUAL,
+		MIPMAPS_AUTO,
+		MIPMAPS_MAX_ENUM
 	};
 	};
 
 
 	struct Slices
 	struct Slices
@@ -222,6 +223,10 @@ public:
 	static bool getConstant(TextureType in, const char *&out);
 	static bool getConstant(TextureType in, const char *&out);
 	static std::vector<std::string> getConstants(TextureType);
 	static std::vector<std::string> getConstants(TextureType);
 
 
+	static bool getConstant(const char *in, MipmapsMode &out);
+	static bool getConstant(MipmapsMode in, const char *&out);
+	static std::vector<std::string> getConstants(MipmapsMode);
+
 protected:
 protected:
 
 
 	void initQuad();
 	void initQuad();

+ 5 - 2
src/modules/graphics/opengl/Canvas.cpp

@@ -373,8 +373,11 @@ love::image::ImageData *Canvas::newImageData(love::image::Image *module, int sli
 
 
 void Canvas::generateMipmaps()
 void Canvas::generateMipmaps()
 {
 {
-	if (getMipmapCount() == 1 || getMipmapMode() == MIPMAPS_NONE)
-		throw love::Exception("generateMipmaps can only be called on a Canvas which was created with mipmaps enabled.");
+	if (getMipmapCount() == 1 || getMipmapsMode() == MIPMAPS_NONE)
+		throw love::Exception("generateMipmaps can only be called on a Texture which was created with mipmaps enabled.");
+
+	if (isPixelFormatCompressed(format))
+		throw love::Exception("generateMipmaps cannot be called on a compressed Texture.");
 
 
 	gl.bindTextureToUnit(this, 0, false);
 	gl.bindTextureToUnit(this, 0, false);
 
 

+ 42 - 37
src/modules/graphics/opengl/Graphics.cpp

@@ -234,7 +234,7 @@ bool Graphics::setMode(int width, int height, int pixelwidth, int pixelheight, b
 
 
 	// Set whether drawing converts input from linear -> sRGB colorspace.
 	// Set whether drawing converts input from linear -> sRGB colorspace.
 	if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
 	if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_sRGB || GLAD_EXT_framebuffer_sRGB
-		|| GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB)
+		|| GLAD_ES_VERSION_3_0)
 	{
 	{
 		if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
 		if (GLAD_VERSION_1_0 || GLAD_EXT_sRGB_write_control)
 			gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, isGammaCorrect());
 			gl.setEnableState(OpenGL::ENABLE_FRAMEBUFFER_SRGB, isGammaCorrect());
@@ -312,11 +312,11 @@ void Graphics::unSetMode()
 	for (const auto &pair : framebufferObjects)
 	for (const auto &pair : framebufferObjects)
 		gl.deleteFramebuffer(pair.second);
 		gl.deleteFramebuffer(pair.second);
 
 
-	for (auto temp : temporaryCanvases)
-		temp.canvas->release();
+	for (auto temp : temporaryTextures)
+		temp.texture->release();
 
 
 	framebufferObjects.clear();
 	framebufferObjects.clear();
-	temporaryCanvases.clear();
+	temporaryTextures.clear();
 
 
 	if (mainVAO != 0)
 	if (mainVAO != 0)
 	{
 	{
@@ -518,7 +518,7 @@ void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pix
 	flushStreamDraws();
 	flushStreamDraws();
 	endPass();
 	endPass();
 
 
-	bool iswindow = rts.getFirstTarget().canvas == nullptr;
+	bool iswindow = rts.getFirstTarget().texture == nullptr;
 	vertex::Winding vertexwinding = state.winding;
 	vertex::Winding vertexwinding = state.winding;
 
 
 	if (iswindow)
 	if (iswindow)
@@ -560,7 +560,7 @@ void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pix
 void Graphics::endPass()
 void Graphics::endPass()
 {
 {
 	auto &rts = states.back().renderTargets;
 	auto &rts = states.back().renderTargets;
-	love::graphics::Canvas *depthstencil = rts.depthStencil.canvas.get();
+	love::graphics::Texture *depthstencil = rts.depthStencil.texture.get();
 
 
 	// Discard the depth/stencil buffer if we're using an internal cached one.
 	// Discard the depth/stencil buffer if we're using an internal cached one.
 	if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
 	if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
@@ -568,15 +568,16 @@ void Graphics::endPass()
 
 
 	// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
 	// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
 	// don't have to worry about resolving to slices.
 	// don't have to worry about resolving to slices.
-	if (rts.colors.size() > 0 && rts.colors[0].canvas->getMSAA() > 1)
+	if (rts.colors.size() > 0 && rts.colors[0].texture->getMSAA() > 1)
 	{
 	{
 		int mip = rts.colors[0].mipmap;
 		int mip = rts.colors[0].mipmap;
-		int w = rts.colors[0].canvas->getPixelWidth(mip);
-		int h = rts.colors[0].canvas->getPixelHeight(mip);
+		int w = rts.colors[0].texture->getPixelWidth(mip);
+		int h = rts.colors[0].texture->getPixelHeight(mip);
 
 
 		for (int i = 0; i < (int) rts.colors.size(); i++)
 		for (int i = 0; i < (int) rts.colors.size(); i++)
 		{
 		{
-			Canvas *c = (Canvas *) rts.colors[i].canvas.get();
+			// FIXME
+			Canvas *c = (Canvas *) rts.colors[i].texture.get();
 
 
 			if (!c->isReadable())
 			if (!c->isReadable())
 				continue;
 				continue;
@@ -619,13 +620,14 @@ void Graphics::endPass()
 
 
 	for (const auto &rt : rts.colors)
 	for (const auto &rt : rts.colors)
 	{
 	{
-		if (rt.canvas->getMipmapMode() == Canvas::MIPMAPS_AUTO && rt.mipmap == 0)
-			rt.canvas->generateMipmaps();
+		// TODO
+//		if (rt.texture->getMipmapMode() == Canvas::MIPMAPS_AUTO && rt.mipmap == 0)
+//			rt.texture->generateMipmaps();
 	}
 	}
 
 
-	int dsmipmap = rts.depthStencil.mipmap;
-	if (depthstencil != nullptr && depthstencil->getMipmapMode() == Canvas::MIPMAPS_AUTO && dsmipmap == 0)
-		depthstencil->generateMipmaps();
+//	int dsmipmap = rts.depthStencil.mipmap;
+//	if (depthstencil != nullptr && depthstencil->getMipmapMode() == Canvas::MIPMAPS_AUTO && dsmipmap == 0)
+//		depthstencil->generateMipmaps();
 }
 }
 
 
 void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
 void Graphics::clear(OptionalColorf c, OptionalInt stencil, OptionalDouble depth)
@@ -818,25 +820,28 @@ void Graphics::discard(OpenGL::FramebufferTarget target, const std::vector<bool>
 		glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]);
 		glDiscardFramebufferEXT(gltarget, (GLint) attachments.size(), &attachments[0]);
 }
 }
 
 
-void Graphics::cleanupCanvas(Canvas *canvas)
+void Graphics::cleanupCanvas(Canvas *texture)
 {
 {
+	if (!texture->isRenderTarget())
+		return;
+
 	for (auto it = framebufferObjects.begin(); it != framebufferObjects.end(); /**/)
 	for (auto it = framebufferObjects.begin(); it != framebufferObjects.end(); /**/)
 	{
 	{
-		bool hascanvas = false;
+		bool hastexture = false;
 		const auto &rts = it->first;
 		const auto &rts = it->first;
 
 
 		for (const RenderTarget &rt : rts.colors)
 		for (const RenderTarget &rt : rts.colors)
 		{
 		{
-			if (rt.canvas == canvas)
+			if (rt.texture == texture)
 			{
 			{
-				hascanvas = true;
+				hastexture = true;
 				break;
 				break;
 			}
 			}
 		}
 		}
 
 
-		hascanvas = hascanvas || rts.depthStencil.canvas == canvas;
+		hastexture = hastexture || rts.depthStencil.texture == texture;
 
 
-		if (hascanvas)
+		if (hastexture)
 		{
 		{
 			if (isCreated())
 			if (isCreated())
 				gl.deleteFramebuffer(it->second);
 				gl.deleteFramebuffer(it->second);
@@ -857,8 +862,8 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
 	}
 	}
 	else
 	else
 	{
 	{
-		int msaa = targets.getFirstTarget().canvas->getMSAA();
-		bool hasDS = targets.depthStencil.canvas != nullptr;
+		int msaa = targets.getFirstTarget().texture->getMSAA();
+		bool hasDS = targets.depthStencil.texture != nullptr;
 
 
 		glGenFramebuffers(1, &fbo);
 		glGenFramebuffers(1, &fbo);
 		gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
 		gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
@@ -868,9 +873,9 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
 
 
 		auto attachCanvas = [&](const RenderTarget &rt)
 		auto attachCanvas = [&](const RenderTarget &rt)
 		{
 		{
-			bool renderbuffer = msaa > 1 || !rt.canvas->isReadable();
+			bool renderbuffer = msaa > 1 || !rt.texture->isReadable();
 			bool srgb = false;
 			bool srgb = false;
-			OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(rt.canvas->getPixelFormat(), renderbuffer, srgb);
+			OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(rt.texture->getPixelFormat(), renderbuffer, srgb);
 
 
 			if (fmt.framebufferAttachments[0] == GL_COLOR_ATTACHMENT0)
 			if (fmt.framebufferAttachments[0] == GL_COLOR_ATTACHMENT0)
 			{
 			{
@@ -879,7 +884,7 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
 				ncolortargets++;
 				ncolortargets++;
 			}
 			}
 
 
-			GLuint handle = (GLuint) rt.canvas->getRenderTargetHandle();
+			GLuint handle = (GLuint) rt.texture->getRenderTargetHandle();
 
 
 			for (GLenum attachment : fmt.framebufferAttachments)
 			for (GLenum attachment : fmt.framebufferAttachments)
 			{
 			{
@@ -889,7 +894,7 @@ void Graphics::bindCachedFBO(const RenderTargets &targets)
 					glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, handle);
 					glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, handle);
 				else
 				else
 				{
 				{
-					TextureType textype = rt.canvas->getTextureType();
+					TextureType textype = rt.texture->getTextureType();
 
 
 					int layer = textype == TEXTURE_CUBE ? 0 : rt.slice;
 					int layer = textype == TEXTURE_CUBE ? 0 : rt.slice;
 					int face = textype == TEXTURE_CUBE ? rt.slice : 0;
 					int face = textype == TEXTURE_CUBE ? rt.slice : 0;
@@ -1056,20 +1061,20 @@ void Graphics::present(void *screenshotCallbackData)
 	// Reset the per-frame stat counts.
 	// Reset the per-frame stat counts.
 	drawCalls = 0;
 	drawCalls = 0;
 	gl.stats.shaderSwitches = 0;
 	gl.stats.shaderSwitches = 0;
-	canvasSwitchCount = 0;
+	renderTargetSwitchCount = 0;
 	drawCallsBatched = 0;
 	drawCallsBatched = 0;
 
 
-	// This assumes temporary canvases will only be used within a render pass.
-	for (int i = (int) temporaryCanvases.size() - 1; i >= 0; i--)
+	// This assumes temporary textures will only be used within a render pass.
+	for (int i = (int) temporaryTextures.size() - 1; i >= 0; i--)
 	{
 	{
-		if (temporaryCanvases[i].framesSinceUse >= MAX_TEMPORARY_CANVAS_UNUSED_FRAMES)
+		if (temporaryTextures[i].framesSinceUse >= MAX_TEMPORARY_TEXTURE_UNUSED_FRAMES)
 		{
 		{
-			temporaryCanvases[i].canvas->release();
-			temporaryCanvases[i] = temporaryCanvases.back();
-			temporaryCanvases.pop_back();
+			temporaryTextures[i].texture->release();
+			temporaryTextures[i] = temporaryTextures.back();
+			temporaryTextures.pop_back();
 		}
 		}
 		else
 		else
-			temporaryCanvases[i].framesSinceUse++;
+			temporaryTextures[i].framesSinceUse++;
 	}
 	}
 }
 }
 
 
@@ -1111,11 +1116,11 @@ void Graphics::setScissor()
 void Graphics::drawToStencilBuffer(StencilAction action, int value)
 void Graphics::drawToStencilBuffer(StencilAction action, int value)
 {
 {
 	const auto &rts = states.back().renderTargets;
 	const auto &rts = states.back().renderTargets;
-	love::graphics::Canvas *dscanvas = rts.depthStencil.canvas.get();
+	love::graphics::Texture *dstexture = rts.depthStencil.texture.get();
 
 
 	if (!isCanvasActive() && !windowHasStencil)
 	if (!isCanvasActive() && !windowHasStencil)
 		throw love::Exception("The window must have stenciling enabled to draw to the main screen's stencil buffer.");
 		throw love::Exception("The window must have stenciling enabled to draw to the main screen's stencil buffer.");
-	else if (isCanvasActive() && (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) == 0 && (dscanvas == nullptr || !isPixelFormatStencil(dscanvas->getPixelFormat())))
+	else if (isCanvasActive() && (rts.temporaryRTFlags & TEMPORARY_RT_STENCIL) == 0 && (dstexture == nullptr || !isPixelFormatStencil(dstexture->getPixelFormat())))
 		throw love::Exception("Drawing to the stencil buffer with a Canvas active requires either stencil=true or a custom stencil-type Canvas to be used, in setCanvas.");
 		throw love::Exception("Drawing to the stencil buffer with a Canvas active requires either stencil=true or a custom stencil-type Canvas to be used, in setCanvas.");
 
 
 	flushStreamDraws();
 	flushStreamDraws();

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

@@ -126,7 +126,7 @@ private:
 			for (size_t i = 0; i < rts.colors.size(); i++)
 			for (size_t i = 0; i < rts.colors.size(); i++)
 				hashtargets[hashcount++] = rts.colors[i];
 				hashtargets[hashcount++] = rts.colors[i];
 
 
-			if (rts.depthStencil.canvas != nullptr)
+			if (rts.depthStencil.texture != nullptr)
 				hashtargets[hashcount++] = rts.depthStencil;
 				hashtargets[hashcount++] = rts.depthStencil;
 			else if (rts.temporaryRTFlags != 0)
 			else if (rts.temporaryRTFlags != 0)
 				hashtargets[hashcount++] = RenderTarget(nullptr, -1, rts.temporaryRTFlags);
 				hashtargets[hashcount++] = RenderTarget(nullptr, -1, rts.temporaryRTFlags);

+ 2 - 18
src/modules/graphics/opengl/Image.cpp

@@ -148,7 +148,7 @@ void Image::loadData()
 			d = std::max(d / 2, 1);
 			d = std::max(d / 2, 1);
 	}
 	}
 
 
-	if (mipmapsType == MIPMAPS_GENERATED)
+	if (getMipmapCount() > 1 && slices.getMipmapCount() <= 1)
 		generateMipmaps();
 		generateMipmaps();
 }
 }
 
 
@@ -200,22 +200,11 @@ bool Image::loadVolatile()
 
 
 	OpenGL::TempDebugGroup debuggroup("Image load");
 	OpenGL::TempDebugGroup debuggroup("Image load");
 
 
-	if (!isCompressed())
-	{
-		// GL_EXT_sRGB doesn't support glGenerateMipmap for sRGB textures.
-		if (sRGB && (GLAD_ES_VERSION_2_0 && GLAD_EXT_sRGB && !GLAD_ES_VERSION_3_0)
-			&& mipmapsType != MIPMAPS_DATA)
-		{
-			mipmapsType = MIPMAPS_NONE;
-			samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
-		}
-	}
-
 	// NPOT textures don't support mipmapping without full NPOT support.
 	// NPOT textures don't support mipmapping without full NPOT support.
 	if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
 	if ((GLAD_ES_VERSION_2_0 && !(GLAD_ES_VERSION_3_0 || GLAD_OES_texture_npot))
 		&& (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight)))
 		&& (pixelWidth != nextP2(pixelWidth) || pixelHeight != nextP2(pixelHeight)))
 	{
 	{
-		mipmapsType = MIPMAPS_NONE;
+		mipmapCount = 1;
 		samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
 		samplerState.mipmapFilter = SamplerState::MIPMAP_FILTER_NONE;
 	}
 	}
 
 
@@ -231,11 +220,6 @@ bool Image::loadVolatile()
 
 
 	setSamplerState(samplerState);
 	setSamplerState(samplerState);
 
 
-	GLenum gltextype = OpenGL::getGLTextureType(texType);
-
-	if (mipmapsType == MIPMAPS_NONE && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_1_0))
-		glTexParameteri(gltextype, GL_TEXTURE_MAX_LEVEL, 0);
-
 	while (glGetError() != GL_NO_ERROR); // Clear errors.
 	while (glGetError() != GL_NO_ERROR); // Clear errors.
 
 
 	try
 	try

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

@@ -1794,10 +1794,10 @@ bool OpenGL::isPixelFormatSupported(PixelFormat pixelformat, bool rendertarget,
 					   && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB));
 					   && (GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB));
 			}
 			}
 			else
 			else
-				return GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB;
+				return GLAD_ES_VERSION_3_0;
 		}
 		}
 		else
 		else
-			return GLAD_ES_VERSION_3_0 || GLAD_EXT_sRGB || GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB;
+			return GLAD_ES_VERSION_3_0 || GLAD_VERSION_2_1 || GLAD_EXT_texture_sRGB;
 	case PIXELFORMAT_R16_UNORM:
 	case PIXELFORMAT_R16_UNORM:
 	case PIXELFORMAT_RG16_UNORM:
 	case PIXELFORMAT_RG16_UNORM:
 		return GLAD_VERSION_3_0
 		return GLAD_VERSION_3_0

+ 9 - 9
src/modules/graphics/wrap_Canvas.cpp

@@ -37,7 +37,7 @@ int w_Canvas_renderTo(lua_State *L)
 
 
 	int startidx = 2;
 	int startidx = 2;
 
 
-	if (rt.canvas->getTextureType() != TEXTURE_2D)
+	if (rt.texture->getTextureType() != TEXTURE_2D)
 	{
 	{
 		rt.slice = (int) luaL_checkinteger(L, 2) - 1;
 		rt.slice = (int) luaL_checkinteger(L, 2) - 1;
 		startidx++;
 		startidx++;
@@ -53,10 +53,10 @@ int w_Canvas_renderTo(lua_State *L)
 		Graphics::RenderTargets oldtargets = graphics->getCanvas();
 		Graphics::RenderTargets oldtargets = graphics->getCanvas();
 
 
 		for (auto c : oldtargets.colors)
 		for (auto c : oldtargets.colors)
-			c.canvas->retain();
+			c.texture->retain();
 
 
-		if (oldtargets.depthStencil.canvas != nullptr)
-			oldtargets.depthStencil.canvas->retain();
+		if (oldtargets.depthStencil.texture != nullptr)
+			oldtargets.depthStencil.texture->retain();
 
 
 		luax_catchexcept(L, [&](){ graphics->setCanvas(rt, false); });
 		luax_catchexcept(L, [&](){ graphics->setCanvas(rt, false); });
 
 
@@ -66,10 +66,10 @@ int w_Canvas_renderTo(lua_State *L)
 		graphics->setCanvas(oldtargets);
 		graphics->setCanvas(oldtargets);
 
 
 		for (auto c : oldtargets.colors)
 		for (auto c : oldtargets.colors)
-			c.canvas->release();
+			c.texture->release();
 
 
-		if (oldtargets.depthStencil.canvas != nullptr)
-			oldtargets.depthStencil.canvas->release();
+		if (oldtargets.depthStencil.texture != nullptr)
+			oldtargets.depthStencil.texture->release();
 
 
 		if (status != 0)
 		if (status != 0)
 			return lua_error(L);
 			return lua_error(L);
@@ -119,8 +119,8 @@ int w_Canvas_getMipmapMode(lua_State *L)
 {
 {
 	Canvas *c = luax_checkcanvas(L, 1);
 	Canvas *c = luax_checkcanvas(L, 1);
 	const char *str;
 	const char *str;
-	if (!Canvas::getConstant(c->getMipmapMode(), str))
-		return luax_enumerror(L, "mipmap mode", Canvas::getConstants(Canvas::MIPMAPS_MAX_ENUM), str);
+	if (!Texture::getConstant(c->getMipmapsMode(), str))
+		return luax_enumerror(L, "mipmap mode", Texture::getConstants(Texture::MIPMAPS_MAX_ENUM), str);
 
 
 	lua_pushstring(L, str);
 	lua_pushstring(L, str);
 	return 1;
 	return 1;

+ 16 - 16
src/modules/graphics/wrap_Graphics.cpp

@@ -252,7 +252,7 @@ static Graphics::RenderTarget checkRenderTarget(lua_State *L, int idx)
 	Graphics::RenderTarget target(luax_checkcanvas(L, -1), 0);
 	Graphics::RenderTarget target(luax_checkcanvas(L, -1), 0);
 	lua_pop(L, 1);
 	lua_pop(L, 1);
 
 
-	TextureType type = target.canvas->getTextureType();
+	TextureType type = target.texture->getTextureType();
 	if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
 	if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
 		target.slice = luax_checkintflag(L, idx, "layer") - 1;
 		target.slice = luax_checkintflag(L, idx, "layer") - 1;
 	else if (type == TEXTURE_CUBE)
 	else if (type == TEXTURE_CUBE)
@@ -294,7 +294,7 @@ int w_setCanvas(lua_State *L)
 			{
 			{
 				targets.colors.emplace_back(luax_checkcanvas(L, -1), 0);
 				targets.colors.emplace_back(luax_checkcanvas(L, -1), 0);
 
 
-				if (targets.colors.back().canvas->getTextureType() != TEXTURE_2D)
+				if (targets.colors.back().texture->getTextureType() != TEXTURE_2D)
 					return luaL_error(L, "Non-2D canvases must use the table-of-tables variant of setCanvas.");
 					return luaL_error(L, "Non-2D canvases must use the table-of-tables variant of setCanvas.");
 			}
 			}
 
 
@@ -311,13 +311,13 @@ int w_setCanvas(lua_State *L)
 		else if (dstype == LUA_TBOOLEAN)
 		else if (dstype == LUA_TBOOLEAN)
 			targets.temporaryRTFlags |= luax_toboolean(L, -1) ? (tempdepthflag | tempstencilflag) : 0;
 			targets.temporaryRTFlags |= luax_toboolean(L, -1) ? (tempdepthflag | tempstencilflag) : 0;
 		else if (dstype != LUA_TNONE && dstype != LUA_TNIL)
 		else if (dstype != LUA_TNONE && dstype != LUA_TNIL)
-			targets.depthStencil.canvas = luax_checkcanvas(L, -1);
+			targets.depthStencil.texture = luax_checkcanvas(L, -1);
 		lua_pop(L, 1);
 		lua_pop(L, 1);
 
 
-		if (targets.depthStencil.canvas == nullptr && (targets.temporaryRTFlags & tempdepthflag) == 0)
+		if (targets.depthStencil.texture == nullptr && (targets.temporaryRTFlags & tempdepthflag) == 0)
 			targets.temporaryRTFlags |= luax_boolflag(L, 1, "depth", false) ? tempdepthflag : 0;
 			targets.temporaryRTFlags |= luax_boolflag(L, 1, "depth", false) ? tempdepthflag : 0;
 
 
-		if (targets.depthStencil.canvas == nullptr && (targets.temporaryRTFlags & tempstencilflag) == 0)
+		if (targets.depthStencil.texture == nullptr && (targets.temporaryRTFlags & tempstencilflag) == 0)
 			targets.temporaryRTFlags |= luax_boolflag(L, 1, "stencil", false) ? tempstencilflag : 0;
 			targets.temporaryRTFlags |= luax_boolflag(L, 1, "stencil", false) ? tempstencilflag : 0;
 	}
 	}
 	else
 	else
@@ -325,7 +325,7 @@ int w_setCanvas(lua_State *L)
 		for (int i = 1; i <= lua_gettop(L); i++)
 		for (int i = 1; i <= lua_gettop(L); i++)
 		{
 		{
 			Graphics::RenderTarget target(luax_checkcanvas(L, i), 0);
 			Graphics::RenderTarget target(luax_checkcanvas(L, i), 0);
-			TextureType type = target.canvas->getTextureType();
+			TextureType type = target.texture->getTextureType();
 
 
 			if (i == 1 && type != TEXTURE_2D)
 			if (i == 1 && type != TEXTURE_2D)
 			{
 			{
@@ -348,7 +348,7 @@ int w_setCanvas(lua_State *L)
 	}
 	}
 
 
 	luax_catchexcept(L, [&]() {
 	luax_catchexcept(L, [&]() {
-		if (targets.getFirstTarget().canvas != nullptr)
+		if (targets.getFirstTarget().texture != nullptr)
 			instance()->setCanvas(targets);
 			instance()->setCanvas(targets);
 		else
 		else
 			instance()->setCanvas();
 			instance()->setCanvas();
@@ -361,10 +361,10 @@ static void pushRenderTarget(lua_State *L, const Graphics::RenderTarget &rt)
 {
 {
 	lua_createtable(L, 1, 2);
 	lua_createtable(L, 1, 2);
 
 
-	luax_pushtype(L, rt.canvas);
+	luax_pushtype(L, rt.texture);
 	lua_rawseti(L, -2, 1);
 	lua_rawseti(L, -2, 1);
 
 
-	TextureType type = rt.canvas->getTextureType();
+	TextureType type = rt.texture->getTextureType();
 
 
 	if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
 	if (type == TEXTURE_2D_ARRAY || type == TEXTURE_VOLUME)
 	{
 	{
@@ -392,13 +392,13 @@ int w_getCanvas(lua_State *L)
 		return 1;
 		return 1;
 	}
 	}
 
 
-	bool shouldUseTablesVariant = targets.depthStencil.canvas != nullptr;
+	bool shouldUseTablesVariant = targets.depthStencil.texture != nullptr;
 
 
 	if (!shouldUseTablesVariant)
 	if (!shouldUseTablesVariant)
 	{
 	{
 		for (const auto &rt : targets.colors)
 		for (const auto &rt : targets.colors)
 		{
 		{
-			if (rt.mipmap != 0 || rt.canvas->getTextureType() != TEXTURE_2D)
+			if (rt.mipmap != 0 || rt.texture->getTextureType() != TEXTURE_2D)
 			{
 			{
 				shouldUseTablesVariant = true;
 				shouldUseTablesVariant = true;
 				break;
 				break;
@@ -416,7 +416,7 @@ int w_getCanvas(lua_State *L)
 			lua_rawseti(L, -2, i + 1);
 			lua_rawseti(L, -2, i + 1);
 		}
 		}
 
 
-		if (targets.depthStencil.canvas != nullptr)
+		if (targets.depthStencil.texture != nullptr)
 		{
 		{
 			pushRenderTarget(L, targets.depthStencil);
 			pushRenderTarget(L, targets.depthStencil);
 			lua_setfield(L, -2, "depthstencil");
 			lua_setfield(L, -2, "depthstencil");
@@ -427,7 +427,7 @@ int w_getCanvas(lua_State *L)
 	else
 	else
 	{
 	{
 		for (const auto &rt : targets.colors)
 		for (const auto &rt : targets.colors)
-			luax_pushtype(L, rt.canvas);
+			luax_pushtype(L, rt.texture);
 
 
 		return ntargets;
 		return ntargets;
 	}
 	}
@@ -1240,8 +1240,8 @@ int w_newCanvas(lua_State *L)
 		if (!lua_isnoneornil(L, -1))
 		if (!lua_isnoneornil(L, -1))
 		{
 		{
 			const char *str = luaL_checkstring(L, -1);
 			const char *str = luaL_checkstring(L, -1);
-			if (!Canvas::getConstant(str, settings.mipmaps))
-				return luax_enumerror(L, "Canvas mipmap mode", Canvas::getConstants(settings.mipmaps), str);
+			if (!Texture::getConstant(str, settings.mipmaps))
+				return luax_enumerror(L, "Texture mipmap mode", Texture::getConstants(settings.mipmaps), str);
 		}
 		}
 		lua_pop(L, 1);
 		lua_pop(L, 1);
 	}
 	}
@@ -2388,7 +2388,7 @@ int w_getStats(lua_State *L)
 	lua_pushinteger(L, stats.drawCallsBatched);
 	lua_pushinteger(L, stats.drawCallsBatched);
 	lua_setfield(L, -2, "drawcallsbatched");
 	lua_setfield(L, -2, "drawcallsbatched");
 
 
-	lua_pushinteger(L, stats.canvasSwitches);
+	lua_pushinteger(L, stats.renderTargetSwitches);
 	lua_setfield(L, -2, "canvasswitches");
 	lua_setfield(L, -2, "canvasswitches");
 
 
 	lua_pushinteger(L, stats.shaderSwitches);
 	lua_pushinteger(L, stats.shaderSwitches);

+ 1 - 1
src/modules/graphics/wrap_Image.cpp

@@ -40,7 +40,7 @@ int w_Image_replacePixels(lua_State *L)
 	int mipmap = 0;
 	int mipmap = 0;
 	int x = 0;
 	int x = 0;
 	int y = 0;
 	int y = 0;
-	bool reloadmipmaps = i->getMipmapsType() == Image::MIPMAPS_GENERATED;
+	bool reloadmipmaps = false; // TODO i->getMipmapsSource() == Texture::MIPMAPS_SOURCE_GENERATED;
 
 
 	if (i->getTextureType() != TEXTURE_2D)
 	if (i->getTextureType() != TEXTURE_2D)
 		slice = (int) luaL_checkinteger(L, 3) - 1;
 		slice = (int) luaL_checkinteger(L, 3) - 1;