Browse Source

metal: initial work on windowing and instance creation

Alex Szpakowski 5 years ago
parent
commit
d687b4c099

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

@@ -32,6 +32,7 @@
 #include "Video.h"
 #include "Text.h"
 #include "common/deprecation.h"
+#include "common/config.h"
 
 // C++
 #include <algorithm>
@@ -106,6 +107,36 @@ love::Type Graphics::type("graphics", &Module::type);
 
 Graphics::DefaultShaderCode Graphics::defaultShaderCode[Shader::STANDARD_MAX_ENUM][Shader::LANGUAGE_MAX_ENUM][2];
 
+namespace opengl { extern love::graphics::Graphics *createInstance(); }
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+namespace metal { extern love::graphics::Graphics *createInstance(); }
+#endif
+
+Graphics *Graphics::createInstance(const std::vector<Renderer> &renderers)
+{
+	Graphics *instance = Module::getInstance<Graphics>(M_GRAPHICS);
+
+	if (instance != nullptr)
+		instance->retain();
+	else
+	{
+		for (auto renderer : renderers)
+		{
+			if (renderer == RENDERER_OPENGL)
+				instance = opengl::createInstance();
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+			if (renderer == RENDERER_METAL)
+				instance = metal::createInstance();
+#endif
+
+			if (instance != nullptr)
+				break;
+		}
+	}
+
+	return instance;
+}
+
 Graphics::Graphics()
 	: width(0)
 	, height(0)
@@ -241,7 +272,7 @@ ShaderStage *Graphics::newShaderStage(ShaderStage::StageType stage, const std::s
 
 	if (s == nullptr)
 	{
-		s = newShaderStageInternal(stage, cachekey, source, getRenderer() == RENDERER_OPENGLES);
+		s = newShaderStageInternal(stage, cachekey, source, usesGLSLES());
 		if (!cachekey.empty())
 			cachedShaderStages[stage][cachekey] = s;
 	}

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

@@ -151,7 +151,7 @@ public:
 	enum Renderer
 	{
 		RENDERER_OPENGL = 0,
-		RENDERER_OPENGLES,
+		RENDERER_METAL,
 		RENDERER_MAX_ENUM
 	};
 
@@ -797,6 +797,11 @@ public:
 	 **/
 	virtual Renderer getRenderer() const = 0;
 
+	/**
+	 * Whether shaders will use GLSL ES or not (mobile shaders).
+	 **/
+	virtual bool usesGLSLES() const = 0;
+
 	/**
 	 * Returns system-dependent renderer information.
 	 * Returned strings can vary greatly between systems! Do not rely on it for
@@ -853,6 +858,8 @@ public:
 		return (T *) scratchBuffer.data();
 	}
 
+	static Graphics *createInstance(const std::vector<Renderer> &renderers);
+
 	static bool getConstant(const char *in, DrawMode &out);
 	static bool getConstant(DrawMode in, const char *&out);
 	static std::vector<std::string> getConstants(DrawMode);

+ 1 - 0
src/modules/graphics/metal/Graphics.h

@@ -88,6 +88,7 @@ public:
 	bool isCanvasFormatSupported(PixelFormat format, bool readable) const override;
 	bool isImageFormatSupported(PixelFormat format, bool sRGB) const override;
 	Renderer getRenderer() const override;
+	bool usesGLSLES() const override;
 	RendererInfo getRendererInfo() const override;
 
 	Shader::Language getShaderLanguageTarget() const override;

+ 40 - 0
src/modules/graphics/metal/Graphics.mm

@@ -133,6 +133,22 @@ static MTLBlendFactor getMTLBlendFactor(BlendFactor factor)
 	return MTLBlendFactorZero;
 }
 
+love::graphics::Graphics *createInstance()
+{
+	love::graphics::Graphics *instance = nullptr;
+
+	try
+	{
+		instance = new Graphics();
+	}
+	catch (love::Exception &e)
+	{
+		printf("Cannot create Metal renderer: %s\n", e.what());
+	}
+
+	return instance;
+}
+
 Graphics::Graphics()
 	: device(nil)
 	, commandQueue(nil)
@@ -935,6 +951,30 @@ void Graphics::setBlendState(const BlendState &blend)
 	}
 }
 
+Graphics::Renderer Graphics::getRenderer() const
+{
+	return RENDERER_METAL;
+}
+
+bool Graphics::usesGLSLES() const
+{
+#ifdef LOVE_IOS
+	return true;
+#else
+	return false;
+#endif
+}
+
+Graphics::RendererInfo Graphics::getRendererInfo() const
+{
+	RendererInfo info;
+	info.name = "Metal";
+	info.version = "1"; // TODO
+	info.vendor = ""; // TODO
+	info.device = device.name.UTF8String;
+	return info;
+}
+
 } // metal
 } // graphics
 } // love

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

@@ -88,6 +88,22 @@ static GLenum getGLBlendFactor(BlendFactor factor)
 	return 0;
 }
 
+love::graphics::Graphics *createInstance()
+{
+	love::graphics::Graphics *instance = nullptr;
+
+	try
+	{
+		instance = new Graphics();
+	}
+	catch (love::Exception &e)
+	{
+		printf("Cannot create OpenGL renderer: %s\n", e.what());
+	}
+
+	return instance;
+}
+
 Graphics::Graphics()
 	: windowHasStencil(false)
 	, mainVAO(0)
@@ -1322,7 +1338,12 @@ void Graphics::setWireframe(bool enable)
 
 Graphics::Renderer Graphics::getRenderer() const
 {
-	return GLAD_ES_VERSION_2_0 ? RENDERER_OPENGLES : RENDERER_OPENGL;
+	return RENDERER_OPENGL;
+}
+
+bool Graphics::usesGLSLES() const
+{
+	return GLAD_ES_VERSION_2_0;
 }
 
 Graphics::RendererInfo Graphics::getRendererInfo() const

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

@@ -108,6 +108,7 @@ public:
 	bool isCanvasFormatSupported(PixelFormat format, bool readable) const override;
 	bool isImageFormatSupported(PixelFormat format, bool sRGB) const override;
 	Renderer getRenderer() const override;
+	bool usesGLSLES() const override;
 	RendererInfo getRendererInfo() const override;
 
 	Shader::Language getShaderLanguageTarget() const override;

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

@@ -1363,7 +1363,7 @@ static int w_getShaderSource(lua_State *L, int startidx, bool gles, std::string
 
 int w_newShader(lua_State *L)
 {
-	bool gles = instance()->getRenderer() == Graphics::RENDERER_OPENGLES;
+	bool gles = instance()->usesGLSLES();
 
 	std::string vertexsource, pixelsource;
 	w_getShaderSource(L, 1, gles, vertexsource, pixelsource);
@@ -3146,13 +3146,22 @@ static const lua_CFunction types[] =
 
 extern "C" int luaopen_love_graphics(lua_State *L)
 {
-	Graphics *instance = instance();
+	Graphics *instance = nullptr;
+
+	{
+		std::vector<Graphics::Renderer> renderers;
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+		renderers.push_back(Graphics::RENDERER_METAL);
+#endif
+		renderers.push_back(Graphics::RENDERER_OPENGL);
+		instance = Graphics::createInstance(renderers);
+	}
+
 	if (instance == nullptr)
 	{
-		luax_catchexcept(L, [&](){ instance = new love::graphics::opengl::Graphics(); });
+		printf("Cannot create graphics: no supported renderer on this system.\n");
+		return luaL_error(L, "Cannot create graphics: no supported renderer on this system.");
 	}
-	else
-		instance->retain();
 
 	WrappedModule w;
 	w.module = instance;

+ 114 - 68
src/modules/window/sdl/Window.cpp

@@ -63,7 +63,10 @@ Window::Window()
 	: open(false)
 	, mouseGrabbed(false)
 	, window(nullptr)
-	, context(nullptr)
+	, glcontext(nullptr)
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+	, metalView(nullptr)
+#endif
 	, displayedWindowError(false)
 	, hasSDL203orEarlier(false)
 	, contextAttribs()
@@ -82,9 +85,7 @@ Window::Window()
 Window::~Window()
 {
 	close(false);
-
 	graphics.set(nullptr);
-
 	SDL_QuitSubSystem(SDL_INIT_VIDEO);
 }
 
@@ -288,9 +289,9 @@ std::vector<Window::ContextAttribs> Window::getContextAttribsList() const
 	return attribslist;
 }
 
-bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, int msaa, bool stencil, int depth)
+bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer, int msaa, bool stencil, int depth)
 {
-	std::vector<ContextAttribs> attribslist = getContextAttribsList();
+	bool needsglcontext = (windowflags & SDL_WINDOW_OPENGL) != 0;
 
 	std::string windowerror;
 	std::string contexterror;
@@ -302,12 +303,12 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 	// Also, apparently some Intel drivers on Windows give back a Microsoft
 	// OpenGL 1.1 software renderer context when high MSAA values are requested!
 
-	const auto create = [&](ContextAttribs attribs) -> bool
+	const auto create = [&](const ContextAttribs *attribs) -> bool
 	{
-		if (context)
+		if (glcontext)
 		{
-			SDL_GL_DeleteContext(context);
-			context = nullptr;
+			SDL_GL_DeleteContext(glcontext);
+			glcontext = nullptr;
 		}
 
 		if (window)
@@ -325,80 +326,112 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 			return false;
 		}
 
-		context = SDL_GL_CreateContext(window);
+		if (attribs != nullptr)
+		{
+			glcontext = SDL_GL_CreateContext(window);
 
-		if (!context)
-			contexterror = std::string(SDL_GetError());
+			if (!glcontext)
+				contexterror = std::string(SDL_GetError());
 
-		// Make sure the context's version is at least what we requested.
-		if (context && !checkGLVersion(attribs, glversion))
-		{
-			SDL_GL_DeleteContext(context);
-			context = nullptr;
-		}
+			// Make sure the context's version is at least what we requested.
+			if (glcontext && !checkGLVersion(*attribs, glversion))
+			{
+				SDL_GL_DeleteContext(glcontext);
+				glcontext = nullptr;
+			}
 
-		if (!context)
-		{
-			SDL_DestroyWindow(window);
-			window = nullptr;
-			return false;
+			if (!glcontext)
+			{
+				SDL_DestroyWindow(window);
+				window = nullptr;
+				return false;
+			}
 		}
 
 		return true;
 	};
 
-	// Try each context profile in order.
-	for (ContextAttribs attribs : attribslist)
+	if (renderer == graphics::Graphics::RENDERER_OPENGL)
 	{
-		int curMSAA  = msaa;
-		bool curSRGB = love::graphics::isGammaCorrect();
+		std::vector<ContextAttribs> attribslist = getContextAttribsList();
 
-		setGLFramebufferAttributes(curMSAA, curSRGB, stencil, depth);
-		setGLContextAttributes(attribs);
+		// Try each context profile in order.
+		for (ContextAttribs attribs : attribslist)
+		{
+			int curMSAA  = msaa;
+			bool curSRGB = love::graphics::isGammaCorrect();
 
-		windowerror.clear();
-		contexterror.clear();
+			setGLFramebufferAttributes(curMSAA, curSRGB, stencil, depth);
+			setGLContextAttributes(attribs);
 
-		create(attribs);
+			windowerror.clear();
+			contexterror.clear();
 
-		if (!window && curMSAA > 0)
-		{
-			// The MSAA setting could have caused the failure.
-			setGLFramebufferAttributes(0, curSRGB, stencil, depth);
-			if (create(attribs))
-				curMSAA = 0;
-		}
+			create(&attribs);
 
-		if (!window && curSRGB)
-		{
-			// same with sRGB.
-			setGLFramebufferAttributes(curMSAA, false, stencil, depth);
-			if (create(attribs))
-				curSRGB = false;
-		}
+			if (!window && curMSAA > 0)
+			{
+				// The MSAA setting could have caused the failure.
+				setGLFramebufferAttributes(0, curSRGB, stencil, depth);
+				if (create(&attribs))
+					curMSAA = 0;
+			}
 
-		if (!window && curMSAA > 0 && curSRGB)
-		{
-			// Or both!
-			setGLFramebufferAttributes(0, false, stencil, depth);
-			if (create(attribs))
+			if (!window && curSRGB)
 			{
-				curMSAA = 0;
-				curSRGB = false;
+				// same with sRGB.
+				setGLFramebufferAttributes(curMSAA, false, stencil, depth);
+				if (create(&attribs))
+					curSRGB = false;
+			}
+
+			if (!window && curMSAA > 0 && curSRGB)
+			{
+				// Or both!
+				setGLFramebufferAttributes(0, false, stencil, depth);
+				if (create(&attribs))
+				{
+					curMSAA = 0;
+					curSRGB = false;
+				}
+			}
+
+			if (window && glcontext)
+			{
+				// Store the successful context attributes so we can re-use them in
+				// subsequent calls to createWindowAndContext.
+				contextAttribs = attribs;
+				love::graphics::setGammaCorrect(curSRGB);
+				break;
 			}
 		}
+	}
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+	else if (renderer == graphics::Graphics::RENDERER_METAL)
+	{
+		if (create(nullptr) && window != nullptr)
+			metalView = SDL_Metal_CreateView(window);
 
-		if (window && context)
+		if (metalView == nullptr && window != nullptr)
 		{
-			// Store the successful context attributes so we can re-use them in
-			// subsequent calls to createWindowAndContext.
-			contextAttribs = attribs;
-			love::graphics::setGammaCorrect(curSRGB);
-			break;
+			SDL_DestroyWindow(window);
+			window = nullptr;
+			// TODO: error message
 		}
 	}
+#endif
+	else
+	{
+		create(nullptr);
+	}
+
+	bool failed = window == nullptr;
+	failed |= (needsglcontext && !glcontext);
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+
+#endif
 
-	if (!context || !window)
+	if (failed)
 	{
 		std::string title = "Unable to create OpenGL window";
 		std::string message = "This program requires a graphics card and video drivers which support OpenGL 2.1 or OpenGL ES 2.";
@@ -435,6 +468,8 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 	if (graphics.get() && graphics->isCanvasActive())
 		throw love::Exception("love.window.setMode cannot be called while a Canvas is active in love.graphics.");
 
+	auto renderer = graphics->getRenderer();
+
 	WindowSettings f;
 
 	if (settings)
@@ -454,7 +489,10 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		height = mode.h;
 	}
 
-	Uint32 sdlflags = SDL_WINDOW_OPENGL;
+	Uint32 sdlflags = 0;
+
+	if (renderer == graphics::Graphics::RENDERER_OPENGL)
+		sdlflags |= SDL_WINDOW_OPENGL;
 
 	// On Android we always must have fullscreen type FULLSCREEN_TYPE_DESKTOP
 #ifdef LOVE_ANDROID
@@ -515,7 +553,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 
 	close();
 
-	if (!createWindowAndContext(x, y, width, height, sdlflags, f.msaa, f.stencil, f.depth))
+	if (!createWindowAndContext(x, y, width, height, sdlflags, renderer, f.msaa, f.stencil, f.depth))
 		return false;
 
 	// Make sure the window keeps any previously set icon.
@@ -670,12 +708,20 @@ void Window::close(bool allowExceptions)
 		graphics->unSetMode();
 	}
 
-	if (context)
+	if (glcontext)
 	{
-		SDL_GL_DeleteContext(context);
-		context = nullptr;
+		SDL_GL_DeleteContext(glcontext);
+		glcontext = nullptr;
 	}
 
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+	if (metalView)
+	{
+		SDL_Metal_DestroyView(metalView);
+		metalView = nullptr;
+	}
+#endif
+
 	if (window)
 	{
 		SDL_DestroyWindow(window);
@@ -726,7 +772,7 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
 
 	if (SDL_SetWindowFullscreen(window, sdlflags) == 0)
 	{
-		SDL_GL_MakeCurrent(window, context);
+		SDL_GL_MakeCurrent(window, glcontext);
 		updateSettings(newsettings, true);
 
 		// Apparently this gets un-set when we exit fullscreen (at least in OS X).
@@ -960,7 +1006,7 @@ love::image::ImageData *Window::getIcon()
 
 void Window::setVSync(int vsync)
 {
-	if (context == nullptr)
+	if (glcontext == nullptr)
 		return;
 
 	SDL_GL_SetSwapInterval(vsync);
@@ -973,7 +1019,7 @@ void Window::setVSync(int vsync)
 
 int Window::getVSync() const
 {
-	return context != nullptr ? SDL_GL_GetSwapInterval() : 0;
+	return glcontext != nullptr ? SDL_GL_GetSwapInterval() : 0;
 }
 
 void Window::setDisplaySleepEnabled(bool enable)

+ 8 - 2
src/modules/window/sdl/Window.h

@@ -23,6 +23,8 @@
 
 // LOVE
 #include "window/Window.h"
+#include "common/config.h"
+#include "graphics/Graphics.h"
 
 // SDL
 #include <SDL.h>
@@ -143,7 +145,7 @@ private:
 	void setGLContextAttributes(const ContextAttribs &attribs);
 	bool checkGLVersion(const ContextAttribs &attribs, std::string &outversion);
 	std::vector<ContextAttribs> getContextAttribsList() const;
-	bool createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, int msaa, bool stencil, int depth);
+	bool createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer, int msaa, bool stencil, int depth);
 
 	// Update the saved window settings based on the window's actual state.
 	void updateSettings(const WindowSettings &newsettings, bool updateGraphicsViewport);
@@ -164,7 +166,11 @@ private:
 	bool mouseGrabbed;
 
 	SDL_Window *window;
-	SDL_GLContext context;
+
+	SDL_GLContext glcontext;
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+	SDL_MetalView metalView;
+#endif
 
 	bool displayedWindowError;
 	bool hasSDL203orEarlier;