Browse Source

metal: use a fallback for fonts when luminance-alpha isn't supported.

Alex Szpakowski 3 years ago
parent
commit
1cf0073cf4

+ 50 - 9
src/modules/graphics/Font.cpp

@@ -82,6 +82,10 @@ Font::Font(love::font::Rasterizer *r, const SamplerState &s)
 	pixelFormat = gd->getFormat();
 	gd->release();
 
+	auto gfx = Module::getInstance<Graphics>(Module::M_GRAPHICS);
+	if (pixelFormat == PIXELFORMAT_LA8_UNORM && !gfx->isPixelFormatSupported(pixelFormat, PIXELFORMATUSAGEFLAGS_SAMPLE))
+		pixelFormat = PIXELFORMAT_RGBA8_UNORM;
+
 	if (!r->hasGlyph(9)) // No tab character in the Rasterizer.
 		useSpacesAsTab = true;
 
@@ -158,18 +162,30 @@ void Font::createTexture()
 	texture->setSamplerState(samplerState);
 
 	{
-		size_t bpp = getPixelFormatBlockSize(pixelFormat);
+		size_t datasize = getPixelFormatSliceSize(pixelFormat, size.width, size.height);
 		size_t pixelcount = size.width * size.height;
 
-		// Initialize the texture with transparent white for Luminance-Alpha
-		// formats (since we keep luminance constant and vary alpha in those
-		// glyphs), and transparent black otherwise.
-		std::vector<uint8> emptydata(pixelcount * bpp, 0);
+		// Initialize the texture with transparent white for truetype fonts
+		// (since we keep luminance constant and vary alpha in those glyphs),
+		// and transparent black otherwise.
+		std::vector<uint8> emptydata(datasize, 0);
 
-		if (pixelFormat == PIXELFORMAT_LA8_UNORM)
+		if (rasterizers[0]->getDataType() == font::Rasterizer::DATA_TRUETYPE)
 		{
-			for (size_t i = 0; i < pixelcount; i++)
-				emptydata[i * 2 + 0] = 255;
+			if (pixelFormat == PIXELFORMAT_LA8_UNORM)
+			{
+				for (size_t i = 0; i < pixelcount; i++)
+					emptydata[i * 2 + 0] = 255;
+			}
+			else if (pixelFormat == PIXELFORMAT_RGBA8_UNORM)
+			{
+				for (size_t i = 0; i < pixelcount; i++)
+				{
+					emptydata[i * 4 + 0] = 255;
+					emptydata[i * 4 + 1] = 255;
+					emptydata[i * 4 + 2] = 255;
+				}
+			}
 		}
 
 		Rect rect = {0, 0, size.width, size.height};
@@ -281,7 +297,32 @@ const Font::Glyph &Font::addGlyph(uint32 glyph)
 		g.texture = texture;
 
 		Rect rect = {textureX, textureY, gd->getWidth(), gd->getHeight()};
-		texture->replacePixels(gd->getData(), gd->getSize(), 0, 0, rect, false);
+
+		if (pixelFormat != gd->getFormat())
+		{
+			if (!(pixelFormat == PIXELFORMAT_RGBA8_UNORM && gd->getFormat() == PIXELFORMAT_LA8_UNORM))
+				throw love::Exception("Cannot upload font glyphs to texture atlas: unexpected format conversion.");
+
+			const uint8 *src = (const uint8 *) gd->getData();
+
+			size_t dstsize = getPixelFormatSliceSize(pixelFormat, w, h);
+			std::vector<uint8> dst(dstsize, 0);
+			uint8 *dstdata = dst.data();
+
+			for (int pixel = 0; pixel < w * h; pixel++)
+			{
+				dstdata[pixel * 4 + 0] = src[pixel * 2 + 0];
+				dstdata[pixel * 4 + 1] = src[pixel * 2 + 0];
+				dstdata[pixel * 4 + 2] = src[pixel * 2 + 0];
+				dstdata[pixel * 4 + 3] = src[pixel * 2 + 1];
+			}
+
+			texture->replacePixels(dstdata, dstsize, 0, 0, rect, false);
+		}
+		else
+		{
+			texture->replacePixels(gd->getData(), gd->getSize(), 0, 0, rect, false);
+		}
 
 		double tX     = (double) textureX,     tY      = (double) textureY;
 		double tWidth = (double) textureWidth, tHeight = (double) textureHeight;

+ 6 - 3
src/modules/graphics/metal/Graphics.mm

@@ -1813,7 +1813,6 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags
 		format = getSRGBPixelFormat(format);
 
 	const uint32 sample = PIXELFORMATUSAGEFLAGS_SAMPLE;
-	const uint32 linear = PIXELFORMATUSAGEFLAGS_LINEAR;
 	const uint32 rt = PIXELFORMATUSAGEFLAGS_RENDERTARGET;
 	const uint32 blend = PIXELFORMATUSAGEFLAGS_BLEND;
 	const uint32 msaa = PIXELFORMATUSAGEFLAGS_MSAA;
@@ -1858,8 +1857,12 @@ bool Graphics::isPixelFormatSupported(PixelFormat format, PixelFormatUsageFlags
 			flags |= all;
 			break;
 		case PIXELFORMAT_LA8_UNORM:
-			// TODO
-			flags |= commonsample;
+			// Requires texture swizzle support.
+			if (@available(macOS 10.15, iOS 13, *))
+			{
+				if (families.apple[1] || families.mac[2] || families.macCatalyst[2])
+					flags |= commonsample;
+			}
 			break;
 		case PIXELFORMAT_RG16_UNORM:
 			if (families.apple[1])

+ 2 - 3
src/modules/graphics/metal/Metal.h

@@ -36,14 +36,13 @@ class Metal
 {
 public:
 
-	struct API_AVAILABLE(macos(10.15), ios(13.0)) PixelFormatDesc
+	struct PixelFormatDesc
 	{
 		MTLPixelFormat format;
 		bool swizzled = false;
-		MTLTextureSwizzleChannels swizzle;
+		API_AVAILABLE(macos(10.15), ios(13.0)) MTLTextureSwizzleChannels swizzle;
 	};
 
-	API_AVAILABLE(macos(10.15), ios(13.0))
 	static PixelFormatDesc convertPixelFormat(id<MTLDevice> device, PixelFormat format, bool &isSRGB);
 
 }; // Metal

+ 10 - 5
src/modules/graphics/metal/Metal.mm

@@ -143,11 +143,16 @@ Metal::PixelFormatDesc Metal::convertPixelFormat(id<MTLDevice> device, PixelForm
 		break;
 
 	case PIXELFORMAT_LA8_UNORM:
-		// TODO: fall back to RGBA8 when swizzle isn't available. Pixel format
-		// size calculation will need to be adjusted as well
-		mtlformat = MTLPixelFormatRG8Unorm;
-		desc.swizzled = true;
-		desc.swizzle = MTLTextureSwizzleChannelsMake(MTLTextureSwizzleRed, MTLTextureSwizzleRed, MTLTextureSwizzleRed, MTLTextureSwizzleGreen);
+		// Only supported on some systems.
+		if (@available(macOS 10.15, iOS 13, *))
+		{
+			mtlformat = MTLPixelFormatRG8Unorm;
+			desc.swizzled = true;
+			desc.swizzle.red = MTLTextureSwizzleRed;
+			desc.swizzle.green = MTLTextureSwizzleRed;
+			desc.swizzle.blue = MTLTextureSwizzleRed;
+			desc.swizzle.alpha = MTLTextureSwizzleGreen;
+		}
 		break;
 
 	case PIXELFORMAT_RGBA4_UNORM:

+ 3 - 8
src/modules/graphics/metal/Shader.mm

@@ -1064,14 +1064,9 @@ id<MTLRenderPipelineState> Shader::getCachedRenderPipeline(const RenderPipelineK
 
 		MTLRenderPipelineColorAttachmentDescriptor *attachment = desc.colorAttachments[i];
 
-		if (@available(macOS 10.15, iOS 13.0, *))
-		{
-			// We already don't really support metal on older systems, this just
-			// silences a compiler warning about it.
-			bool isSRGB = false;
-			auto formatdesc = Metal::convertPixelFormat(device, format, isSRGB);
-			attachment.pixelFormat = formatdesc.format;
-		}
+		bool isSRGB = false;
+		auto formatdesc = Metal::convertPixelFormat(device, format, isSRGB);
+		attachment.pixelFormat = formatdesc.format;
 
 		if (key.blend.enable)
 		{

+ 8 - 8
src/modules/graphics/metal/Texture.mm

@@ -61,17 +61,17 @@ Texture::Texture(love::graphics::Graphics *gfxbase, id<MTLDevice> device, const
 	desc.arrayLength = layers;
 	desc.mipmapLevelCount = mipmapCount;
 	desc.textureType = getMTLTextureType(texType, 1);
-	if (@available(macOS 10.15, iOS 13, *))
+
+	auto formatdesc = Metal::convertPixelFormat(device, format, sRGB);
+	desc.pixelFormat = formatdesc.format;
+	if (formatdesc.swizzled)
 	{
-		// We already don't really support metal on older systems, this just
-		// silences a compiler warning about it.
-		auto formatdesc = Metal::convertPixelFormat(device, format, sRGB);
-		desc.pixelFormat = formatdesc.format;
-		if (formatdesc.swizzled)
+		// Swizzled formats are already only used on supported systems, this
+		// just silences a compiler warning about it.
+		if (@available(macOS 10.15, iOS 13, *))
 			desc.swizzle = formatdesc.swizzle;
 	}
-	else
-		throw love::Exception("Metal backend is only supported on macOS 10.15+ and iOS 13+.");
+
 	desc.storageMode = MTLStorageModePrivate;
 
 	if (readable)