Browse Source

Clean up some common Image and Canvas code.

Alex Szpakowski 5 years ago
parent
commit
6c447ab4f6

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

@@ -72,7 +72,7 @@ Canvas::Canvas(const Settings &settings)
 	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 	const Graphics::Capabilities &caps = gfx->getCapabilities();
 	const Graphics::Capabilities &caps = gfx->getCapabilities();
 
 
-	if (!gfx->isCanvasFormatSupported(format, readable))
+	if (!gfx->isPixelFormatSupported(format, true, readable, false))
 	{
 	{
 		const char *fstr = "rgba8";
 		const char *fstr = "rgba8";
 		const char *readablestr = "";
 		const char *readablestr = "";

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

@@ -694,7 +694,7 @@ void Graphics::setCanvas(const RenderTargets &rts)
 		PixelFormat dsformat = PIXELFORMAT_STENCIL8;
 		PixelFormat dsformat = PIXELFORMAT_STENCIL8;
 		if (wantsdepth && wantsstencil)
 		if (wantsdepth && wantsstencil)
 			dsformat = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
 			dsformat = PIXELFORMAT_DEPTH24_UNORM_STENCIL8;
-		else if (wantsdepth && isCanvasFormatSupported(PIXELFORMAT_DEPTH24_UNORM, false))
+		else if (wantsdepth && isPixelFormatSupported(PIXELFORMAT_DEPTH24_UNORM, true, false, false))
 			dsformat = PIXELFORMAT_DEPTH24_UNORM;
 			dsformat = PIXELFORMAT_DEPTH24_UNORM;
 		else if (wantsdepth)
 		else if (wantsdepth)
 			dsformat = PIXELFORMAT_DEPTH16_UNORM;
 			dsformat = PIXELFORMAT_DEPTH16_UNORM;

+ 7 - 5
src/modules/graphics/Graphics.h

@@ -785,12 +785,14 @@ public:
 	const Capabilities &getCapabilities() const;
 	const Capabilities &getCapabilities() const;
 
 
 	/**
 	/**
-	 * Gets whether the specified pixel format is supported by Canvases or
-	 * Images.
+	 * Converts PIXELFORMAT_NORMAL and PIXELFORMAT_HDR into a real format.
 	 **/
 	 **/
-	virtual bool isCanvasFormatSupported(PixelFormat format) const = 0;
-	virtual bool isCanvasFormatSupported(PixelFormat format, bool readable) const = 0;
-	virtual bool isImageFormatSupported(PixelFormat format, bool sRGB = false) const = 0;
+	virtual PixelFormat getSizedFormat(PixelFormat format) const = 0;
+
+	/**
+	 * Gets whether the specified pixel format is supported.
+	 **/
+	virtual bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) = 0;
 
 
 	/**
 	/**
 	 * Gets the renderer used by love.graphics.
 	 * Gets the renderer used by love.graphics.

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

@@ -79,7 +79,7 @@ Image::~Image()
 void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
 void Image::init(PixelFormat fmt, int w, int h, const Settings &settings)
 {
 {
 	Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
 	Graphics *gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
-	if (gfx != nullptr && !gfx->isImageFormatSupported(fmt, sRGB))
+	if (gfx != nullptr && !gfx->isPixelFormatSupported(fmt, false, true, sRGB))
 	{
 	{
 		const char *str;
 		const char *str;
 		if (love::getConstant(fmt, str))
 		if (love::getConstant(fmt, str))

+ 3 - 143
src/modules/graphics/opengl/Canvas.cpp

@@ -196,7 +196,9 @@ Canvas::Canvas(const Settings &settings)
     , renderbuffer(0)
     , renderbuffer(0)
 	, actualSamples(0)
 	, actualSamples(0)
 {
 {
-	format = getSizedFormat(format);
+	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
+	if (gfx != nullptr)
+		format = gfx->getSizedFormat(format);
 
 
 	initQuad();
 	initQuad();
 	loadVolatile();
 	loadVolatile();
@@ -474,153 +476,11 @@ void Canvas::generateMipmaps()
 	glGenerateMipmap(gltextype);
 	glGenerateMipmap(gltextype);
 }
 }
 
 
-PixelFormat Canvas::getSizedFormat(PixelFormat format)
-{
-	switch (format)
-	{
-	case PIXELFORMAT_NORMAL:
-		if (isGammaCorrect())
-			return PIXELFORMAT_sRGBA8_UNORM;
-		else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8_UNORM, true, true, false))
-			// 32-bit render targets don't have guaranteed support on GLES2.
-			return PIXELFORMAT_RGBA4_UNORM;
-		else
-			return PIXELFORMAT_RGBA8_UNORM;
-	case PIXELFORMAT_HDR:
-		return PIXELFORMAT_RGBA16_FLOAT;
-	default:
-		return format;
-	}
-}
-
-bool Canvas::isSupported()
-{
-	return GLAD_ES_VERSION_2_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object || GLAD_EXT_framebuffer_object;
-}
-
 bool Canvas::isMultiFormatMultiCanvasSupported()
 bool Canvas::isMultiFormatMultiCanvasSupported()
 {
 {
 	return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
 	return gl.getMaxRenderTargets() > 1 && (GLAD_ES_VERSION_3_0 || GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object);
 }
 }
 
 
-Canvas::SupportedFormat Canvas::supportedFormats[] = {};
-Canvas::SupportedFormat Canvas::checkedFormats[] = {};
-
-bool Canvas::isFormatSupported(PixelFormat format)
-{
-	return isFormatSupported(format, !isPixelFormatDepthStencil(format));
-}
-
-bool Canvas::isFormatSupported(PixelFormat format, bool readable)
-{
-	if (!isSupported())
-		return false;
-
-	const char *fstr = "?";
-	love::getConstant(format, fstr);
-
-	bool supported = true;
-	format = getSizedFormat(format);
-
-	if (!OpenGL::isPixelFormatSupported(format, true, readable, false))
-		return false;
-
-	if (checkedFormats[format].get(readable))
-		return supportedFormats[format].get(readable);
-
-	// Even though we might have the necessary OpenGL version or extension,
-	// drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
-	// a texture to a FBO whose format the driver doesn't like. So we should
-	// test with an actual FBO.
-	GLuint texture = 0;
-	GLuint renderbuffer = 0;
-
-	// Avoid the test for depth/stencil formats - not every GL version
-	// guarantees support for depth/stencil-only render targets (which we would
-	// need for the test below to work), and we already do some finagling in
-	// convertPixelFormat to try to use the best-supported internal
-	// depth/stencil format for a particular driver.
-	if (isPixelFormatDepthStencil(format))
-	{
-		checkedFormats[format].set(readable, true);
-		supportedFormats[format].set(readable, true);
-		return true;
-	}
-
-	bool unusedSRGB = false;
-	OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, readable, unusedSRGB);
-
-	GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
-
-	GLuint fbo = 0;
-	glGenFramebuffers(1, &fbo);
-	gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
-
-	// Make sure at least something is bound to a color attachment. I believe
-	// this is required on ES2 but I'm not positive.
-	if (isPixelFormatDepthStencil(format))
-		gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, 0, 0);
-
-	if (readable)
-	{
-		glGenTextures(1, &texture);
-		gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
-
-		Texture::Filter f;
-		f.min = f.mag = Texture::FILTER_NEAREST;
-		gl.setTextureFilter(TEXTURE_2D, f);
-
-		Texture::Wrap w;
-		gl.setTextureWrap(TEXTURE_2D, w);
-
-		unusedSRGB = false;
-		gl.rawTexStorage(TEXTURE_2D, 1, format, unusedSRGB, 1, 1);
-	}
-	else
-	{
-		glGenRenderbuffers(1, &renderbuffer);
-		glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
-		glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
-	}
-
-	for (GLenum attachment : fmt.framebufferAttachments)
-	{
-		if (attachment == GL_NONE)
-			continue;
-
-		if (readable)
-			gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0);
-		else
-			glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
-	}
-
-	supported = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
-
-	gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
-	gl.deleteFramebuffer(fbo);
-
-	if (texture != 0)
-		gl.deleteTexture(texture);
-
-	if (renderbuffer != 0)
-		glDeleteRenderbuffers(1, &renderbuffer);
-
-	// Cache the result so we don't do this for every isFormatSupported call.
-	checkedFormats[format].set(readable, true);
-	supportedFormats[format].set(readable, supported);
-
-	return supported;
-}
-
-void Canvas::resetFormatSupport()
-{
-	for (int i = 0; i < (int)PIXELFORMAT_MAX_ENUM; i++)
-	{
-		checkedFormats[i].readable = false;
-		checkedFormats[i].nonreadable = false;
-	}
-}
-
 } // opengl
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

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

@@ -71,34 +71,10 @@ public:
 		return fbo;
 		return fbo;
 	}
 	}
 
 
-	static PixelFormat getSizedFormat(PixelFormat format);
-	static bool isSupported();
 	static bool isMultiFormatMultiCanvasSupported();
 	static bool isMultiFormatMultiCanvasSupported();
-	static bool isFormatSupported(PixelFormat format, bool readable);
-	static bool isFormatSupported(PixelFormat format);
-	static void resetFormatSupport();
 
 
 private:
 private:
 
 
-	struct SupportedFormat
-	{
-		bool readable = false;
-		bool nonreadable = false;
-
-		bool get(bool getreadable)
-		{
-			return getreadable ? readable : nonreadable;
-		}
-
-		void set(bool setreadable, bool val)
-		{
-			if (setreadable)
-				readable = val;
-			else
-				nonreadable = val;
-		}
-	};
-
 	GLuint fbo;
 	GLuint fbo;
 
 
 	GLuint texture;
 	GLuint texture;
@@ -108,9 +84,6 @@ private:
 
 
 	int actualSamples;
 	int actualSamples;
 
 
-	static SupportedFormat supportedFormats[PIXELFORMAT_MAX_ENUM];
-	static SupportedFormat checkedFormats[PIXELFORMAT_MAX_ENUM];
-
 }; // Canvas
 }; // Canvas
 
 
 } // opengl
 } // opengl

+ 117 - 9
src/modules/graphics/opengl/Graphics.cpp

@@ -91,9 +91,9 @@ static GLenum getGLBlendFactor(BlendFactor factor)
 Graphics::Graphics()
 Graphics::Graphics()
 	: windowHasStencil(false)
 	: windowHasStencil(false)
 	, mainVAO(0)
 	, mainVAO(0)
+	, supportedFormats()
 {
 {
 	gl = OpenGL();
 	gl = OpenGL();
-	Canvas::resetFormatSupport();
 
 
 	auto window = getInstance<love::window::Window>(M_WINDOW);
 	auto window = getInstance<love::window::Window>(M_WINDOW);
 
 
@@ -1388,19 +1388,127 @@ void Graphics::initCapabilities()
 		capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
 		capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i);
 }
 }
 
 
-bool Graphics::isCanvasFormatSupported(PixelFormat format) const
+PixelFormat Graphics::getSizedFormat(PixelFormat format) const
 {
 {
-	return Canvas::isFormatSupported(format);
+	switch (format)
+	{
+	case PIXELFORMAT_NORMAL:
+		if (isGammaCorrect())
+			return PIXELFORMAT_sRGBA8_UNORM;
+		else if (!OpenGL::isPixelFormatSupported(PIXELFORMAT_RGBA8_UNORM, true, true, false))
+			// 32-bit render targets don't have guaranteed support on GLES2.
+			return PIXELFORMAT_RGBA4_UNORM;
+		else
+			return PIXELFORMAT_RGBA8_UNORM;
+	case PIXELFORMAT_HDR:
+		return PIXELFORMAT_RGBA16_FLOAT;
+	default:
+		return format;
+	}
 }
 }
 
 
-bool Graphics::isCanvasFormatSupported(PixelFormat format, bool readable) const
+bool Graphics::isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB)
 {
 {
-	return Canvas::isFormatSupported(format, readable);
-}
+	format = getSizedFormat(format);
 
 
-bool Graphics::isImageFormatSupported(PixelFormat format, bool sRGB) const
-{
-	return Image::isFormatSupported(format, sRGB);
+	if (sRGB && format == PIXELFORMAT_RGBA8_UNORM)
+	{
+		format = PIXELFORMAT_sRGBA8_UNORM;
+		sRGB = false;
+	}
+
+	OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][sRGB ? 1 : 0];
+
+	if (supported.hasValue)
+		return supported.value;
+
+	if (!OpenGL::isPixelFormatSupported(format, rendertarget, readable, sRGB))
+	{
+		supported.set(false);
+		return supported.value;
+	}
+
+	if (!rendertarget)
+	{
+		supported.set(true);
+		return supported.value;
+	}
+
+	// Even though we might have the necessary OpenGL version or extension,
+	// drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching
+	// a texture to a FBO whose format the driver doesn't like. So we should
+	// test with an actual FBO.
+	GLuint texture = 0;
+	GLuint renderbuffer = 0;
+
+	// Avoid the test for depth/stencil formats - not every GL version
+	// guarantees support for depth/stencil-only render targets (which we would
+	// need for the test below to work), and we already do some finagling in
+	// convertPixelFormat to try to use the best-supported internal
+	// depth/stencil format for a particular driver.
+	if (isPixelFormatDepthStencil(format))
+	{
+		supported.set(true);
+		return true;
+	}
+
+	OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, readable, sRGB);
+
+	GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL);
+
+	GLuint fbo = 0;
+	glGenFramebuffers(1, &fbo);
+	gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo);
+
+	// Make sure at least something is bound to a color attachment. I believe
+	// this is required on ES2 but I'm not positive.
+	if (isPixelFormatDepthStencil(format))
+		gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D), 0, 0, 0);
+
+	if (readable)
+	{
+		glGenTextures(1, &texture);
+		gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false);
+
+		Texture::Filter f;
+		f.min = f.mag = Texture::FILTER_NEAREST;
+		gl.setTextureFilter(TEXTURE_2D, f);
+
+		Texture::Wrap w;
+		gl.setTextureWrap(TEXTURE_2D, w);
+
+		gl.rawTexStorage(TEXTURE_2D, 1, format, sRGB, 1, 1);
+	}
+	else
+	{
+		glGenRenderbuffers(1, &renderbuffer);
+		glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
+		glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1);
+	}
+
+	for (GLenum attachment : fmt.framebufferAttachments)
+	{
+		if (attachment == GL_NONE)
+			continue;
+
+		if (readable)
+			gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0);
+		else
+			glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer);
+	}
+
+	supported.set(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
+
+	gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
+	gl.deleteFramebuffer(fbo);
+
+	if (texture != 0)
+		gl.deleteTexture(texture);
+
+	if (renderbuffer != 0)
+		glDeleteRenderbuffers(1, &renderbuffer);
+
+	return supported.value;
 }
 }
 
 
 Shader::Language Graphics::getShaderLanguageTarget() const
 Shader::Language Graphics::getShaderLanguageTarget() const

+ 5 - 3
src/modules/graphics/opengl/Graphics.h

@@ -104,9 +104,8 @@ public:
 
 
 	void setWireframe(bool enable) override;
 	void setWireframe(bool enable) override;
 
 
-	bool isCanvasFormatSupported(PixelFormat format) const override;
-	bool isCanvasFormatSupported(PixelFormat format, bool readable) const override;
-	bool isImageFormatSupported(PixelFormat format, bool sRGB) const override;
+	PixelFormat getSizedFormat(PixelFormat format) const override;
+	bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) override;
 	Renderer getRenderer() const override;
 	Renderer getRenderer() const override;
 	RendererInfo getRendererInfo() const override;
 	RendererInfo getRendererInfo() const override;
 
 
@@ -153,6 +152,9 @@ private:
 	bool windowHasStencil;
 	bool windowHasStencil;
 	GLuint mainVAO;
 	GLuint mainVAO;
 
 
+	// [rendertarget][readable][srgb]
+	OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2];
+
 }; // Graphics
 }; // Graphics
 
 
 } // opengl
 } // opengl

+ 0 - 5
src/modules/graphics/opengl/Image.cpp

@@ -351,11 +351,6 @@ bool Image::setMipmapSharpness(float sharpness)
 	return true;
 	return true;
 }
 }
 
 
-bool Image::isFormatSupported(PixelFormat pixelformat, bool sRGB)
-{
-	return OpenGL::isPixelFormatSupported(pixelformat, false, true, sRGB);
-}
-
 } // opengl
 } // opengl
 } // graphics
 } // graphics
 } // love
 } // love

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

@@ -54,8 +54,6 @@ public:
 
 
 	bool setMipmapSharpness(float sharpness) override;
 	bool setMipmapSharpness(float sharpness) override;
 
 
-	static bool isFormatSupported(PixelFormat pixelformat, bool sRGB);
-
 private:
 private:
 
 
 	void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
 	void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;

+ 5 - 4
src/modules/graphics/wrap_Graphics.cpp

@@ -2282,14 +2282,14 @@ int w_getCanvasFormats(lua_State *L)
 		{
 		{
 			supported = [](PixelFormat format) -> bool
 			supported = [](PixelFormat format) -> bool
 			{
 			{
-				return instance()->isCanvasFormatSupported(format, true);
+				return instance()->isPixelFormatSupported(format, true, true, false);
 			};
 			};
 		}
 		}
 		else
 		else
 		{
 		{
 			supported = [](PixelFormat format) -> bool
 			supported = [](PixelFormat format) -> bool
 			{
 			{
-				return instance()->isCanvasFormatSupported(format, false);
+				return instance()->isPixelFormatSupported(format, true, false, false);
 			};
 			};
 		}
 		}
 	}
 	}
@@ -2297,7 +2297,8 @@ int w_getCanvasFormats(lua_State *L)
 	{
 	{
 		supported = [](PixelFormat format) -> bool
 		supported = [](PixelFormat format) -> bool
 		{
 		{
-			return instance()->isCanvasFormatSupported(format);
+			bool readable = !isPixelFormatDepthStencil(format);
+			return instance()->isPixelFormatSupported(format, true, readable, false);
 		};
 		};
 	}
 	}
 
 
@@ -2308,7 +2309,7 @@ int w_getImageFormats(lua_State *L)
 {
 {
 	const auto supported = [](PixelFormat format) -> bool
 	const auto supported = [](PixelFormat format) -> bool
 	{
 	{
-		return instance()->isImageFormatSupported(format);
+		return instance()->isPixelFormatSupported(format, false, true, false);
 	};
 	};
 
 
 	const auto ignore = [](PixelFormat format) -> bool
 	const auto ignore = [](PixelFormat format) -> bool