Browse Source

Update metal backend for Image+Canvas->Texture changes

Alex Szpakowski 5 years ago
parent
commit
64b38459cf

+ 8 - 19
platform/xcode/liblove.xcodeproj/project.pbxproj

@@ -781,8 +781,6 @@
 		FA18CED723DBC6E000263725 /* Shader.h in Headers */ = {isa = PBXBuildFile; fileRef = FA18CECD23DBC6E000263725 /* Shader.h */; };
 		FA18CED823DBC6E000263725 /* StreamBuffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CECE23DBC6E000263725 /* StreamBuffer.mm */; };
 		FA18CED923DBC6E000263725 /* StreamBuffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CECE23DBC6E000263725 /* StreamBuffer.mm */; };
-		FA18CEDA23DBC6E000263725 /* Canvas.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CECF23DBC6E000263725 /* Canvas.mm */; };
-		FA18CEDB23DBC6E000263725 /* Canvas.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CECF23DBC6E000263725 /* Canvas.mm */; };
 		FA18CEDC23DBC6E000263725 /* Metal.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CED023DBC6E000263725 /* Metal.mm */; };
 		FA18CEDD23DBC6E000263725 /* Metal.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CED023DBC6E000263725 /* Metal.mm */; };
 		FA18CEDE23DBC6E000263725 /* Shader.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CED123DBC6E000263725 /* Shader.mm */; };
@@ -792,13 +790,11 @@
 		FA18CEE223DBC6E000263725 /* Graphics.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CED323DBC6E000263725 /* Graphics.mm */; };
 		FA18CEE323DBC6E000263725 /* Metal.h in Headers */ = {isa = PBXBuildFile; fileRef = FA18CED423DBC6E000263725 /* Metal.h */; };
 		FA18CEE423DBC6E000263725 /* Graphics.h in Headers */ = {isa = PBXBuildFile; fileRef = FA18CED523DBC6E000263725 /* Graphics.h */; };
-		FA18CEE523DBC6E000263725 /* Canvas.h in Headers */ = {isa = PBXBuildFile; fileRef = FA18CED623DBC6E000263725 /* Canvas.h */; };
 		FA18CEE723DBC6F700263725 /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA18CEE623DBC6F700263725 /* Metal.framework */; };
 		FA18CEE923DBC8D400263725 /* Buffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEE823DBC8D400263725 /* Buffer.mm */; };
 		FA18CEEA23DBC8D400263725 /* Buffer.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEE823DBC8D400263725 /* Buffer.mm */; };
-		FA18CEEE23DC9B3E00263725 /* Image.h in Headers */ = {isa = PBXBuildFile; fileRef = FA18CEEC23DC9B3E00263725 /* Image.h */; };
-		FA18CEEF23DC9B3E00263725 /* Image.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEED23DC9B3E00263725 /* Image.mm */; };
-		FA18CEF023DC9B3E00263725 /* Image.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEED23DC9B3E00263725 /* Image.mm */; };
+		FA18CEEF23DC9B3E00263725 /* Texture.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEED23DC9B3E00263725 /* Texture.mm */; };
+		FA18CEF023DC9B3E00263725 /* Texture.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEED23DC9B3E00263725 /* Texture.mm */; };
 		FA18CF1623DCF67900263725 /* spirv_parser.hpp in Headers */ = {isa = PBXBuildFile; fileRef = FA18CEF223DCF67800263725 /* spirv_parser.hpp */; };
 		FA18CF1723DCF67900263725 /* spirv_glsl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEF323DCF67800263725 /* spirv_glsl.cpp */; };
 		FA18CF1823DCF67900263725 /* spirv_glsl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FA18CEF323DCF67800263725 /* spirv_glsl.cpp */; };
@@ -1855,19 +1851,17 @@
 		FA15DFAB1F9B8C850042AB22 /* StringMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringMap.cpp; sourceTree = "<group>"; };
 		FA18CECD23DBC6E000263725 /* Shader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Shader.h; sourceTree = "<group>"; };
 		FA18CECE23DBC6E000263725 /* StreamBuffer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = StreamBuffer.mm; sourceTree = "<group>"; };
-		FA18CECF23DBC6E000263725 /* Canvas.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Canvas.mm; sourceTree = "<group>"; };
 		FA18CED023DBC6E000263725 /* Metal.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Metal.mm; sourceTree = "<group>"; };
 		FA18CED123DBC6E000263725 /* Shader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Shader.mm; sourceTree = "<group>"; };
 		FA18CED223DBC6E000263725 /* StreamBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StreamBuffer.h; sourceTree = "<group>"; };
 		FA18CED323DBC6E000263725 /* Graphics.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Graphics.mm; sourceTree = "<group>"; };
 		FA18CED423DBC6E000263725 /* Metal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Metal.h; sourceTree = "<group>"; };
 		FA18CED523DBC6E000263725 /* Graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Graphics.h; sourceTree = "<group>"; };
-		FA18CED623DBC6E000263725 /* Canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Canvas.h; sourceTree = "<group>"; };
 		FA18CEE623DBC6F700263725 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
 		FA18CEE823DBC8D400263725 /* Buffer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Buffer.mm; sourceTree = "<group>"; };
 		FA18CEEB23DBC8EB00263725 /* Buffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Buffer.h; sourceTree = "<group>"; };
-		FA18CEEC23DC9B3E00263725 /* Image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Image.h; sourceTree = "<group>"; };
-		FA18CEED23DC9B3E00263725 /* Image.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Image.mm; sourceTree = "<group>"; };
+		FA18CEEC23DC9B3E00263725 /* Texture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Texture.h; sourceTree = "<group>"; };
+		FA18CEED23DC9B3E00263725 /* Texture.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Texture.mm; sourceTree = "<group>"; };
 		FA18CEF223DCF67800263725 /* spirv_parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = spirv_parser.hpp; sourceTree = "<group>"; };
 		FA18CEF323DCF67800263725 /* spirv_glsl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spirv_glsl.cpp; sourceTree = "<group>"; };
 		FA18CEF423DCF67800263725 /* spirv_cross_error_handling.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = spirv_cross_error_handling.hpp; sourceTree = "<group>"; };
@@ -3473,12 +3467,8 @@
 			children = (
 				FA18CEEB23DBC8EB00263725 /* Buffer.h */,
 				FA18CEE823DBC8D400263725 /* Buffer.mm */,
-				FA18CED623DBC6E000263725 /* Canvas.h */,
-				FA18CECF23DBC6E000263725 /* Canvas.mm */,
 				FA18CED523DBC6E000263725 /* Graphics.h */,
 				FA18CED323DBC6E000263725 /* Graphics.mm */,
-				FA18CEEC23DC9B3E00263725 /* Image.h */,
-				FA18CEED23DC9B3E00263725 /* Image.mm */,
 				FA18CED423DBC6E000263725 /* Metal.h */,
 				FA18CED023DBC6E000263725 /* Metal.mm */,
 				FA18CECD23DBC6E000263725 /* Shader.h */,
@@ -3487,6 +3477,8 @@
 				FA18CF4423DD1A8000263725 /* ShaderStage.mm */,
 				FA18CED223DBC6E000263725 /* StreamBuffer.h */,
 				FA18CECE23DBC6E000263725 /* StreamBuffer.mm */,
+				FA18CEEC23DC9B3E00263725 /* Texture.h */,
+				FA18CEED23DC9B3E00263725 /* Texture.mm */,
 			);
 			path = metal;
 			sourceTree = "<group>";
@@ -4141,7 +4133,6 @@
 				FA0B7E2F1A95902C000E1D17 /* RopeJoint.h in Headers */,
 				FA0B7D141A95902C000E1D17 /* Font.h in Headers */,
 				FA0B7E591A95902C000E1D17 /* wrap_Joint.h in Headers */,
-				FA18CEE523DBC6E000263725 /* Canvas.h in Headers */,
 				FA0B7A631A958EA3000E1D17 /* b2ContactManager.h in Headers */,
 				FA0B7E771A95902C000E1D17 /* wrap_WeldJoint.h in Headers */,
 				FA0B7A911A958EA3000E1D17 /* b2FrictionJoint.h in Headers */,
@@ -4600,7 +4591,6 @@
 				FA0B7A3F1A958EA3000E1D17 /* b2ChainShape.cpp in Sources */,
 				FA0B7E921A95902C000E1D17 /* ModPlugDecoder.cpp in Sources */,
 				FA0B7E521A95902C000E1D17 /* wrap_FrictionJoint.cpp in Sources */,
-				FA18CEDB23DBC6E000263725 /* Canvas.mm in Sources */,
 				FADF54351E3DAE6E00012CC0 /* wrap_SpriteBatch.cpp in Sources */,
 				FA4F2BB51DE1E4C300CA37D7 /* wrap_RecordingDevice.cpp in Sources */,
 				FA0B7A311A958EA3000E1D17 /* b2CollidePolygon.cpp in Sources */,
@@ -4700,7 +4690,7 @@
 				FA0B7CD41A95902C000E1D17 /* Source.cpp in Sources */,
 				FAA3A9AF1B7D465A00CED060 /* android.cpp in Sources */,
 				FAE64A812071363100BC7981 /* physfs_archiver_dir.c in Sources */,
-				FA18CEF023DC9B3E00263725 /* Image.mm in Sources */,
+				FA18CEF023DC9B3E00263725 /* Texture.mm in Sources */,
 				FA0B7CD11A95902C000E1D17 /* Audio.cpp in Sources */,
 				FA4F2B7B1DE0181B00CA37D7 /* xxhash.c in Sources */,
 				FA0B7D131A95902C000E1D17 /* Font.cpp in Sources */,
@@ -4984,7 +4974,6 @@
 				FA0B7E0F1A95902C000E1D17 /* FrictionJoint.cpp in Sources */,
 				FA620A351AA2F8DB005DB4C2 /* wrap_Texture.cpp in Sources */,
 				FAF140771E20934C00F898D2 /* iomapper.cpp in Sources */,
-				FA18CEDA23DBC6E000263725 /* Canvas.mm in Sources */,
 				FA0B7D091A95902C000E1D17 /* wrap_FileData.cpp in Sources */,
 				FA18CEDC23DBC6E000263725 /* Metal.mm in Sources */,
 				FA0B7B341A958EA3000E1D17 /* wuff_convert.c in Sources */,
@@ -5310,7 +5299,7 @@
 				FA0B7D281A95902C000E1D17 /* wrap_GlyphData.cpp in Sources */,
 				FA0B7DE21A95902C000E1D17 /* wrap_RandomGenerator.cpp in Sources */,
 				FAF6C9F223C2DE2900D7B5BC /* SPVRemapper.cpp in Sources */,
-				FA18CEEF23DC9B3E00263725 /* Image.mm in Sources */,
+				FA18CEEF23DC9B3E00263725 /* Texture.mm in Sources */,
 				FACA02F61F5E396B0084B28F /* wrap_DataModule.cpp in Sources */,
 				FA0B79401A958E3B000E1D17 /* utf8.cpp in Sources */,
 				FA0B79241A958E3B000E1D17 /* Exception.cpp in Sources */,

+ 0 - 76
src/modules/graphics/metal/Canvas.mm

@@ -1,76 +0,0 @@
-/**
- * Copyright (c) 2006-2020 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty.  In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- *    claim that you wrote the original software. If you use this software
- *    in a product, an acknowledgment in the product documentation would be
- *    appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- *    misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
-
-#include "Canvas.h"
-#include "Graphics.h"
-
-namespace love
-{
-namespace graphics
-{
-namespace metal
-{
-
-Canvas::Canvas(id<MTLDevice> device, const Settings &settings)
-	: love::graphics::Canvas(settings)
-{ @autoreleasepool {
-	MTLTextureDescriptor *desc = [MTLTextureDescriptor new];
-
-	// TODO: sampleCount validation
-	desc.sampleCount = getRequestedMSAA();
-
-	desc.width = pixelWidth;
-	desc.height = pixelHeight;
-	desc.depth = depth;
-	desc.arrayLength = layers;
-	desc.mipmapLevelCount = mipmapCount;
-	desc.textureType = Metal::getTextureType(texType, getRequestedMSAA());
-
-	bool sRGB = false;
-	desc.pixelFormat = Metal::convertPixelFormat(format, sRGB);
-
-	desc.storageMode = MTLStorageModePrivate;
-	desc.usage = MTLTextureUsageRenderTarget;
-
-	if (isReadable())
-		desc.usage |= MTLTextureUsageShaderRead;
-
-	texture = [device newTextureWithDescriptor:desc];
-
-	if (texture == nil)
-		throw love::Exception("Out of graphics memory.");
-
-	// TODO: initialize texture to transparent black.
-}}
-
-Canvas::~Canvas()
-{ @autoreleasepool {
-	texture = nil;
-}}
-
-void Canvas::generateMipmaps()
-{ @autoreleasepool {
-	id<MTLBlitCommandEncoder> encoder = Graphics::getInstance()->useBlitEncoder();
-	[encoder generateMipmapsForTexture:texture];
-}}
-
-} // metal
-} // graphics
-} // love

+ 7 - 10
src/modules/graphics/metal/Graphics.h

@@ -40,9 +40,7 @@ public:
 	// Implements Module.
 	const char *getName() const override { return "love.graphics.metal"; }
 
-	love::graphics::Image *newImage(const Image::Slices &data, const Image::Settings &settings) override;
-	love::graphics::Image *newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings) override;
-	love::graphics::Canvas *newCanvas(const Canvas::Settings &settings) override;
+	love::graphics::Texture *newTexture(const Texture::Settings &settings, const Texture::Slices *data = nullptr) override;
 	love::graphics::Buffer *newBuffer(size_t size, const void *data, BufferType type, vertex::Usage usage, uint32 mapflags) override;
 
 	void setViewportSize(int width, int height, int pixelwidth, int pixelheight) override;
@@ -53,7 +51,7 @@ public:
 
 	void draw(const DrawCommand &cmd) override;
 	void draw(const DrawIndexedCommand &cmd) override;
-	void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, Texture *texture) override;
+	void drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture) override;
 
 	void clear(OptionalColorf color, OptionalInt stencil, OptionalDouble depth) override;
 	void clear(const std::vector<OptionalColorf> &colors, OptionalInt stencil, OptionalDouble depth) override;
@@ -83,10 +81,9 @@ public:
 	void setPointSize(float size) 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, bool rendertarget, bool readable, bool sRGB) const override;
+	bool isPixelFormatSupported(PixelFormat format, bool rendertarget, bool readable, bool sRGB = false) override;
 	Renderer getRenderer() const override;
 	bool usesGLSLES() const override;
 	RendererInfo getRendererInfo() const override;
@@ -105,7 +102,7 @@ public:
 	id<MTLBlitCommandEncoder> getBlitEncoder() const { return blitEncoder; }
 	void submitBlitEncoder();
 
-	id<MTLSamplerState> getCachedSampler(const Texture::Filter &f, const Texture::Wrap &w, float maxAnisotropy, Optional<CompareMode> depthSampleMode);
+	id<MTLSamplerState> getCachedSampler(const SamplerState &s);
 
 	static Graphics *getInstance() { return Module::getInstance<Graphics>(M_GRAPHICS); }
 
@@ -164,7 +161,7 @@ private:
 	love::graphics::ShaderStage *newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles) override;
 	love::graphics::Shader *newShaderInternal(love::graphics::ShaderStage *vertex, love::graphics::ShaderStage *pixel) override;
 	love::graphics::StreamBuffer *newStreamBuffer(BufferType type, size_t size) override;
-	void setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) override;
+	void setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas) override;
 	void initCapabilities() override;
 	void getAPIStats(int &shaderswitches) const override;
 

+ 66 - 75
src/modules/graphics/metal/Graphics.mm

@@ -21,8 +21,7 @@
 #include "Graphics.h"
 #include "StreamBuffer.h"
 #include "Buffer.h"
-#include "Canvas.h"
-#include "Image.h"
+#include "Texture.h"
 #include "Shader.h"
 #include "window/Window.h"
 #include "image/Image.h"
@@ -34,44 +33,43 @@ namespace graphics
 namespace metal
 {
 
-static MTLSamplerMinMagFilter getMTLSamplerFilter(Texture::FilterMode mode)
+static MTLSamplerMinMagFilter getMTLSamplerFilter(SamplerState::FilterMode mode)
 {
 	switch (mode)
 	{
-		case Texture::FILTER_NONE: return MTLSamplerMinMagFilterLinear;
-		case Texture::FILTER_LINEAR: return MTLSamplerMinMagFilterLinear;
-		case Texture::FILTER_NEAREST: return MTLSamplerMinMagFilterNearest;
-		case Texture::FILTER_MAX_ENUM: return MTLSamplerMinMagFilterLinear;
+		case SamplerState::FILTER_LINEAR: return MTLSamplerMinMagFilterLinear;
+		case SamplerState::FILTER_NEAREST: return MTLSamplerMinMagFilterNearest;
+		case SamplerState::FILTER_MAX_ENUM: return MTLSamplerMinMagFilterLinear;
 	}
 	return MTLSamplerMinMagFilterLinear;
 }
 
-static MTLSamplerMipFilter getMTLSamplerMipFilter(Texture::FilterMode mode)
+static MTLSamplerMipFilter getMTLSamplerMipFilter(SamplerState::MipmapFilterMode mode)
 {
 	switch (mode)
 	{
-		case Texture::FILTER_NONE: return MTLSamplerMipFilterNotMipmapped;
-		case Texture::FILTER_LINEAR: return MTLSamplerMipFilterLinear;
-		case Texture::FILTER_NEAREST: return MTLSamplerMipFilterNearest;
-		case Texture::FILTER_MAX_ENUM: return MTLSamplerMipFilterNotMipmapped;
+		case SamplerState::MIPMAP_FILTER_NONE: return MTLSamplerMipFilterNotMipmapped;
+		case SamplerState::MIPMAP_FILTER_LINEAR: return MTLSamplerMipFilterLinear;
+		case SamplerState::MIPMAP_FILTER_NEAREST: return MTLSamplerMipFilterNearest;
+		case SamplerState::MIPMAP_FILTER_MAX_ENUM: return MTLSamplerMipFilterNotMipmapped;
 	}
 	return MTLSamplerMipFilterNotMipmapped;
 }
 
-static MTLSamplerAddressMode getMTLSamplerAddressMode(Texture::WrapMode mode)
+static MTLSamplerAddressMode getMTLSamplerAddressMode(SamplerState::WrapMode mode)
 {
 	switch (mode)
 	{
-		case Texture::WRAP_CLAMP: return MTLSamplerAddressModeClampToEdge;
-		case Texture::WRAP_CLAMP_ZERO: return MTLSamplerAddressModeClampToZero;
+		case SamplerState::WRAP_CLAMP: return MTLSamplerAddressModeClampToEdge;
+		case SamplerState::WRAP_CLAMP_ZERO: return MTLSamplerAddressModeClampToZero;
 #ifdef LOVE_MACOS
-		case Texture::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToBorderColor;
+		case SamplerState::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToBorderColor;
 #else
-		case Texture::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToZero;
+		case SamplerState::WRAP_CLAMP_ONE: return MTLSamplerAddressModeClampToZero;
 #endif
-		case Texture::WRAP_REPEAT: return MTLSamplerAddressModeRepeat;
-		case Texture::WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat;
-		case Texture::WRAP_MAX_ENUM: return MTLSamplerAddressModeClampToEdge;
+		case SamplerState::WRAP_REPEAT: return MTLSamplerAddressModeRepeat;
+		case SamplerState::WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat;
+		case SamplerState::WRAP_MAX_ENUM: return MTLSamplerAddressModeClampToEdge;
 	}
 	return MTLSamplerAddressModeClampToEdge;
 }
@@ -202,19 +200,9 @@ love::graphics::StreamBuffer *Graphics::newStreamBuffer(BufferType type, size_t
 	return CreateStreamBuffer(device, type, size);
 }
 
-love::graphics::Image *Graphics::newImage(const Image::Slices &data, const Image::Settings &settings)
+love::graphics::Texture *Graphics::newTexture(const Texture::Settings &settings, const Texture::Slices *data)
 {
-	return new Image(device, data, settings);
-}
-
-love::graphics::Image *Graphics::newImage(TextureType textype, PixelFormat format, int width, int height, int slices, const Image::Settings &settings)
-{
-	return new Image(device, textype, format, width, height, slices, settings);
-}
-
-love::graphics::Canvas *Graphics::newCanvas(const Canvas::Settings &settings)
-{
-	return new Canvas(device, settings);
+	return new Texture(device, settings, data);
 }
 
 love::graphics::ShaderStage *Graphics::newShaderStageInternal(ShaderStage::StageType stage, const std::string &cachekey, const std::string &source, bool gles)
@@ -245,9 +233,9 @@ void Graphics::initCapabilities()
 		}
 	}
 
-	capabilities.features[FEATURE_MULTI_CANVAS_FORMATS] = true;
+	capabilities.features[FEATURE_MULTI_RENDER_TARGET_FORMATS] = true;
 	capabilities.features[FEATURE_CLAMP_ZERO] = true;
-	capabilities.features[FEATURE_BLENDMINMAX] = true;
+	capabilities.features[FEATURE_BLEND_MINMAX] = true;
 	capabilities.features[FEATURE_LIGHTEN] = true;
 	capabilities.features[FEATURE_FULL_NPOT] = true;
 	capabilities.features[FEATURE_PIXEL_SHADER_HIGHP] = true;
@@ -263,8 +251,8 @@ void Graphics::initCapabilities()
 	capabilities.limits[LIMIT_TEXTURE_LAYERS] = 2048;
 	capabilities.limits[LIMIT_VOLUME_TEXTURE_SIZE] = 2048;
 	capabilities.limits[LIMIT_CUBE_TEXTURE_SIZE] = 16384; // TODO
-	capabilities.limits[LIMIT_MULTI_CANVAS] = 8; // TODO
-	capabilities.limits[LIMIT_CANVAS_MSAA] = msaa;
+	capabilities.limits[LIMIT_RENDER_TARGETS] = 8; // TODO
+	capabilities.limits[LIMIT_TEXTURE_MSAA] = msaa;
 	capabilities.limits[LIMIT_ANISOTROPY] = 16.0f;
 	static_assert(LIMIT_MAX_ENUM == 8, "Graphics::initCapabilities must be updated when adding a new system limit!");
 
@@ -279,7 +267,7 @@ void Graphics::setViewportSize(int width, int height, int pixelwidth, int pixelh
 	this->pixelWidth = pixelwidth;
 	this->pixelHeight = pixelheight;
 
-	if (!isCanvasActive())
+	if (!isRenderTargetActive())
 	{
 		dirtyRenderState |= STATEBIT_VIEWPORT | STATEBIT_SCISSOR;
 
@@ -343,10 +331,10 @@ void Graphics::unSetMode()
 
 	submitCommandBuffer();
 
-	for (auto temp : temporaryCanvases)
-		temp.canvas->release();
+	for (auto temp : temporaryTextures)
+		temp.texture->release();
 
-	temporaryCanvases.clear();
+	temporaryTextures.clear();
 
 	created = false;
 }
@@ -418,28 +406,31 @@ void Graphics::submitBlitEncoder()
 	}
 }
 
-id<MTLSamplerState> Graphics::getCachedSampler(const Texture::Filter &f, const Texture::Wrap &w, float maxAnisotropy, Optional<CompareMode> depthSampleMode)
+id<MTLSamplerState> Graphics::getCachedSampler(const SamplerState &s)
 { @autoreleasepool {
 	id<MTLSamplerState> sampler = nil;
 
 	{
 		MTLSamplerDescriptor *desc = [MTLSamplerDescriptor new];
 
-		desc.minFilter = getMTLSamplerFilter(f.min);
-		desc.magFilter = getMTLSamplerFilter(f.mag);
-		desc.mipFilter = getMTLSamplerMipFilter(f.mipmap);
-		desc.maxAnisotropy = std::max(1.0f, std::min(maxAnisotropy, 16.0f));
+		desc.minFilter = getMTLSamplerFilter(s.minFilter);
+		desc.magFilter = getMTLSamplerFilter(s.magFilter);
+		desc.mipFilter = getMTLSamplerMipFilter(s.mipmapFilter);
+		desc.maxAnisotropy = std::max(1.0f, std::min((float)s.maxAnisotropy, 16.0f));
 
-		desc.sAddressMode = getMTLSamplerAddressMode(w.s);
-		desc.tAddressMode = getMTLSamplerAddressMode(w.t);
-		desc.rAddressMode = getMTLSamplerAddressMode(w.r);
+		desc.sAddressMode = getMTLSamplerAddressMode(s.wrapU);
+		desc.tAddressMode = getMTLSamplerAddressMode(s.wrapV);
+		desc.rAddressMode = getMTLSamplerAddressMode(s.wrapW);
 
 #ifdef LOVE_MACOS
 		desc.borderColor = MTLSamplerBorderColorOpaqueWhite;
 #endif
 
-		if (depthSampleMode.hasValue)
-			desc.compareFunction = getMTLCompareFunction(depthSampleMode.value);
+		desc.lodMinClamp = s.minLod;
+		desc.lodMaxClamp = s.maxLod;
+
+		if (s.depthSampleMode.hasValue)
+			desc.compareFunction = getMTLCompareFunction(s.depthSampleMode.value);
 
 		sampler = [device newSamplerStateWithDescriptor:desc];
 	}
@@ -531,10 +522,10 @@ void Graphics::applyRenderState(id<MTLRenderCommandEncoder> encoder)
 		int rth = 0;
 
 		const auto &rt = state.renderTargets.getFirstTarget();
-		if (rt.canvas.get())
+		if (rt.texture.get())
 		{
-			rtw = rt.canvas->getPixelWidth();
-			rth = rt.canvas->getPixelHeight();
+			rtw = rt.texture->getPixelWidth();
+			rth = rt.texture->getPixelHeight();
 		}
 		else
 		{
@@ -653,7 +644,7 @@ void Graphics::draw(const DrawIndexedCommand &cmd)
 					 instanceCount:cmd.instanceCount];
 }}
 
-void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, Texture *texture)
+void Graphics::drawQuads(int start, int count, const vertex::Attributes &attributes, const vertex::BufferBindings &buffers, love::graphics::Texture *texture)
 { @autoreleasepool {
 	const int MAX_VERTICES_PER_DRAW = LOVE_UINT16_MAX;
 	const int MAX_QUADS_PER_DRAW    = MAX_VERTICES_PER_DRAW / 4;
@@ -693,7 +684,7 @@ void Graphics::drawQuads(int start, int count, const vertex::Attributes &attribu
 	}
 }}
 
-void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas)
+void Graphics::setRenderTargetsInternal(const RenderTargets &rts, int w, int h, int pixelw, int pixelh, bool hasSRGBcanvas)
 {
 	const DisplayState &state = states.back();
 	// TODO
@@ -707,7 +698,7 @@ void Graphics::setCanvasInternal(const RenderTargets &rts, int w, int h, int pix
 void Graphics::endPass()
 {
 	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.
 	if (depthstencil == nullptr && (rts.temporaryRTFlags & (TEMPORARY_RT_DEPTH | TEMPORARY_RT_STENCIL)) != 0)
@@ -715,15 +706,15 @@ void Graphics::endPass()
 
 	// Resolve MSAA buffers. MSAA is only supported for 2D render targets so we
 	// 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 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++)
 		{
-			Canvas *c = (Canvas *) rts.colors[i].canvas.get();
+			Texture *c = (Texture *) rts.colors[i].texture.get();
 
 			if (!c->isReadable())
 				continue;
@@ -739,12 +730,12 @@ void Graphics::endPass()
 
 	for (const auto &rt : rts.colors)
 	{
-		if (rt.canvas->getMipmapMode() == Canvas::MIPMAPS_AUTO && rt.mipmap == 0)
-			rt.canvas->generateMipmaps();
+		if (rt.texture->getMipmapsMode() == Texture::MIPMAPS_AUTO && rt.mipmap == 0)
+			rt.texture->generateMipmaps();
 	}
 
 	int dsmipmap = rts.depthStencil.mipmap;
-	if (depthstencil != nullptr && depthstencil->getMipmapMode() == Canvas::MIPMAPS_AUTO && dsmipmap == 0)
+	if (depthstencil != nullptr && depthstencil->getMipmapsMode() == Texture::MIPMAPS_AUTO && dsmipmap == 0)
 		depthstencil->generateMipmaps();
 }
 
@@ -786,8 +777,8 @@ void Graphics::present(void *screenshotCallbackData)
 	if (!isActive())
 		return;
 
-	if (isCanvasActive())
-		throw love::Exception("present cannot be called while a Canvas is active.");
+	if (isRenderTargetActive())
+		throw love::Exception("present cannot be called while a render target is active.");
 
 	deprecations.draw(this);
 
@@ -865,20 +856,20 @@ void Graphics::present(void *screenshotCallbackData)
 	// Reset the per-frame stat counts.
 	drawCalls = 0;
 	//gl.stats.shaderSwitches = 0;
-	canvasSwitchCount = 0;
+	renderTargetSwitchCount = 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--)
+	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
-			temporaryCanvases[i].framesSinceUse++;
+			temporaryTextures[i].framesSinceUse++;
 	}
 }
 
@@ -906,11 +897,11 @@ void Graphics::setScissor()
 void Graphics::drawToStencilBuffer(StencilAction action, int value)
 {
 	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 (!isRenderTargetActive() && !windowHasStencil)
 		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 (isRenderTargetActive() && (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.");
 
 	flushStreamDraws();

+ 0 - 65
src/modules/graphics/metal/Image.h

@@ -1,65 +0,0 @@
-/**
- * Copyright (c) 2006-2020 LOVE Development Team
- *
- * This software is provided 'as-is', without any express or implied
- * warranty.  In no event will the authors be held liable for any damages
- * arising from the use of this software.
- *
- * Permission is granted to anyone to use this software for any purpose,
- * including commercial applications, and to alter it and redistribute it
- * freely, subject to the following restrictions:
- *
- * 1. The origin of this software must not be misrepresented; you must not
- *    claim that you wrote the original software. If you use this software
- *    in a product, an acknowledgment in the product documentation would be
- *    appreciated but is not required.
- * 2. Altered source versions must be plainly marked as such, and must not be
- *    misrepresented as being the original software.
- * 3. This notice may not be removed or altered from any source distribution.
- **/
-
-#pragma once
-
-#include "common/config.h"
-#include "common/Color.h"
-#include "common/int.h"
-#include "graphics/Image.h"
-#include "Metal.h"
-
-namespace love
-{
-namespace graphics
-{
-namespace metal
-{
-
-class Image final : public love::graphics::Image
-{
-public:
-
-	Image(id<MTLDevice> device, const Slices &data, const Settings &settings);
-	Image(id<MTLDevice> device, TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings);
-	virtual ~Image();
-
-	ptrdiff_t getHandle() const override { return (ptrdiff_t) texture; }
-
-	void setFilter(const Texture::Filter &f) override;
-	bool setWrap(const Texture::Wrap &w) override;
-
-	bool setMipmapSharpness(float sharpness) override;
-
-private:
-
-	void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r) override;
-	void generateMipmaps() override;
-
-	void create(id<MTLDevice> device);
-
-	id<MTLTexture> texture;
-	id<MTLSamplerState> sampler;
-
-}; // Image
-
-} // metal
-} // graphics
-} // love

+ 2 - 2
src/modules/graphics/metal/Shader.h

@@ -45,10 +45,10 @@ public:
 	const UniformInfo *getUniformInfo(const std::string &name) const override;
 	const UniformInfo *getUniformInfo(BuiltinUniform builtin) const override;
 	void updateUniform(const UniformInfo *info, int count) override;
-	void sendTextures(const UniformInfo *info, Texture **textures, int count) override;
+	void sendTextures(const UniformInfo *info, love::graphics::Texture **textures, int count) override;
 	bool hasUniform(const std::string &name) const override;
 	ptrdiff_t getHandle() const override;
-	void setVideoTextures(Texture *ytexture, Texture *cbtexture, Texture *crtexture) override;
+	void setVideoTextures(love::graphics::Texture *ytexture, love::graphics::Texture *cbtexture, love::graphics::Texture *crtexture) override;
 
 private:
 

+ 13 - 23
src/modules/graphics/metal/Canvas.h → src/modules/graphics/metal/Texture.h

@@ -23,7 +23,7 @@
 #include "common/config.h"
 #include "common/Color.h"
 #include "common/int.h"
-#include "graphics/Canvas.h"
+#include "graphics/Texture.h"
 #include "Metal.h"
 
 namespace love
@@ -33,39 +33,29 @@ namespace graphics
 namespace metal
 {
 
-class Canvas final : public love::graphics::Canvas
+class Texture final : public love::graphics::Texture
 {
 public:
 
-	Canvas(id<MTLDevice> device, const Settings &settings);
-	virtual ~Canvas();
+	Texture(id<MTLDevice> device, const Settings &settings, const Slices *data);
+	virtual ~Texture();
 
-	// Implements Texture.
-	void setFilter(const Texture::Filter &f) override;
-	bool setWrap(const Texture::Wrap &w) override;
-	bool setMipmapSharpness(float sharpness) override;
-	void setDepthSampleMode(Optional<CompareMode> mode) override;
-	ptrdiff_t getHandle() const override { return (ptrdiff_t) texture; }
-
-	love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
 	void generateMipmaps() override;
+	love::image::ImageData *newImageData(love::image::Image *module, int slice, int mipmap, const Rect &rect) override;
+	void setSamplerState(const SamplerState &s) override;
 
-	int getMSAA() const override
-	{
-		return 0;
-	}
-
-	ptrdiff_t getRenderTargetHandle() const override
-	{
-		return (ptrdiff_t) texture;
-	}
+	ptrdiff_t getHandle() const override { return (ptrdiff_t) texture; }
+	ptrdiff_t getRenderTargetHandle() const override { return (ptrdiff_t) texture; /* TODO */ }
+	int getMSAA() const override { return 1 /* TODO*/; }
 
 private:
 
+	void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override;
+
 	id<MTLTexture> texture;
-	id<MTLTexture> resolveTexture;
+	id<MTLSamplerState> sampler;
 
-}; // Canvas
+}; // Texture
 
 } // metal
 } // graphics

+ 59 - 33
src/modules/graphics/metal/Image.mm → src/modules/graphics/metal/Texture.mm

@@ -18,7 +18,7 @@
  * 3. This notice may not be removed or altered from any source distribution.
  **/
 
-#include "Image.h"
+#include "Texture.h"
 #include "Graphics.h"
 
 namespace love
@@ -28,48 +28,39 @@ namespace graphics
 namespace metal
 {
 
-Image::Image(id<MTLDevice> device, TextureType textype, PixelFormat format, int width, int height, int slices, const Settings &settings)
-	: love::graphics::Image(textype, format, width, height, slices, settings)
+Texture::Texture(id<MTLDevice> device, const Settings &settings, const Slices *data)
+	: love::graphics::Texture(settings, data)
 	, texture(nil)
 	, sampler(nil)
 { @autoreleasepool {
-	create(device);
-}}
+	MTLTextureDescriptor *desc = [MTLTextureDescriptor new];
 
-Image::Image(id<MTLDevice> device, const Slices &slices, const Settings &settings)
-	: love::graphics::Image(slices, settings)
-	, texture(nil)
-	, sampler(nil)
-{ @autoreleasepool {
-	create(device);
-}}
+	int w = pixelWidth;
+	int h = pixelHeight;
 
-Image::~Image()
-{ @autoreleasepool {
-	texture = nil;
-	sampler = nil;
-}}
+	// TODO: sampleCount validation
+	desc.sampleCount = getRequestedMSAA();
 
-void Image::create(id<MTLDevice> device)
-{
-	MTLTextureDescriptor *desc = [MTLTextureDescriptor new];
-
-	desc.width = pixelWidth;
-	desc.height = pixelHeight;
+	desc.width = w;
+	desc.height = h;
 	desc.depth = depth;
 	desc.arrayLength = layers;
 	desc.mipmapLevelCount = mipmapCount;
-	desc.textureType = Metal::getTextureType(texType, 1);
+	desc.textureType = Metal::getTextureType(texType, (int)desc.sampleCount);
 	desc.pixelFormat = Metal::convertPixelFormat(format, sRGB);
-	desc.usage = MTLTextureUsageShaderRead;
 	desc.storageMode = MTLStorageModePrivate;
 
+	if (readable)
+		desc.usage |= MTLTextureUsageShaderRead;
+	if (renderTarget)
+		desc.usage |= MTLTextureUsageRenderTarget;
+
 	texture = [device newTextureWithDescriptor:desc];
 
 	if (texture == nil)
 		throw love::Exception("Out of graphics memory.");
 
-	int mipcount = mipmapsType == MIPMAPS_GENERATED ? 1 : getMipmapCount();
+	int mipcount = getMipmapCount();
 
 	int slicecount = 1;
 	if (texType == TEXTURE_VOLUME)
@@ -83,17 +74,41 @@ void Image::create(id<MTLDevice> device)
 	{
 		for (int slice = 0; slice < slicecount; slice++)
 		{
-			love::image::ImageDataBase *imgd = data.get(slice, mip);
+			auto imgd = data != nullptr ? data->get(slice, mip) : nullptr;
 			if (imgd != nullptr)
 				uploadImageData(imgd, mip, slice, 0, 0);
 		}
 	}
 
-	if (mipmapsType == MIPMAPS_GENERATED)
+	if (data == nullptr || data->get(0, 0) == nullptr)
+	{
+		// Initialize all slices to transparent black.
+		if (!isPixelFormatDepthStencil(format))
+		{
+			std::vector<uint8> emptydata(getPixelFormatSliceSize(format, w, h));
+			Rect r = {0, 0, w, h};
+			for (int i = 0; i < slicecount; i++)
+				uploadByteData(format, emptydata.data(), emptydata.size(), 0, i, r);
+		}
+		else
+		{
+			// TODO
+		}
+	}
+
+	// Non-readable textures can't have mipmaps (enforced in the base class),
+	// so generateMipmaps here is fine - when they aren't already initialized.
+	if (getMipmapCount() > 1 && (data == nullptr || data->getMipmapCount() <= 1))
 		generateMipmaps();
-}
+}}
 
-void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r)
+Texture::~Texture()
+{ @autoreleasepool {
+	texture = nil;
+	sampler = nil;
+}}
+
+void Texture::uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *)
 { @autoreleasepool {
 	auto gfx = Graphics::getInstance();
 	id<MTLBuffer> buffer = [gfx->device newBufferWithBytes:data
@@ -127,10 +142,19 @@ void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t siz
 		break;
 	}
 
+	size_t rowSize = 0;
+	if (isCompressed())
+		rowSize = getPixelFormatCompressedBlockRowSize(format, r.w);
+	else
+		rowSize = getPixelFormatUncompressedRowSize(format, r.w);
+
+	// TODO: Verify this is correct for compressed formats at small sizes.
+	size_t sliceSize = getPixelFormatSliceSize(format, r.w, r.h);
+
 	[encoder copyFromBuffer:buffer
 			   sourceOffset:0
-		  sourceBytesPerRow:getPixelFormatRowStride(pixelformat, r.w)
-		sourceBytesPerImage:0 // TODO?
+		  sourceBytesPerRow:rowSize
+		sourceBytesPerImage:sliceSize
 				 sourceSize:MTLSizeMake(r.w, r.h, 1)
 				  toTexture:texture
 		   destinationSlice:slice
@@ -139,8 +163,10 @@ void Image::uploadByteData(PixelFormat pixelformat, const void *data, size_t siz
 					options:options];
 }}
 
-void Image::generateMipmaps()
+void Texture::generateMipmaps()
 { @autoreleasepool {
+	// TODO: alternate method for non-color-renderable and non-filterable
+	// pixel formats.
 	id<MTLBlitCommandEncoder> encoder = Graphics::getInstance()->useBlitEncoder();
 	[encoder generateMipmapsForTexture:texture];
 }}