Browse Source

metal: hook up the backbuffer

Alex Szpakowski 5 years ago
parent
commit
99b39065b6

+ 4 - 0
src/common/config.h

@@ -124,6 +124,10 @@
 #	define LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK
 #	define LOVE_LEGENDARY_ACCELEROMETER_AS_JOYSTICK_HACK
 #endif
 #endif
 
 
+#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#	define LOVE_GRAPHICS_METAL
+#endif
+
 // Autotools config.h
 // Autotools config.h
 #ifdef HAVE_CONFIG_H
 #ifdef HAVE_CONFIG_H
 #	include <../config.h>
 #	include <../config.h>

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

@@ -23,6 +23,9 @@
 #include "graphics/Graphics.h"
 #include "graphics/Graphics.h"
 #include "Metal.h"
 #include "Metal.h"
 
 
+@class CAMetalLayer;
+@protocol CAMetalDrawable;
+
 namespace love
 namespace love
 {
 {
 namespace graphics
 namespace graphics
@@ -177,6 +180,8 @@ private:
 	id<MTLRenderCommandEncoder> renderEncoder;
 	id<MTLRenderCommandEncoder> renderEncoder;
 	id<MTLBlitCommandEncoder> blitEncoder;
 	id<MTLBlitCommandEncoder> blitEncoder;
 
 
+	CAMetalLayer *metalLayer;
+	id<CAMetalDrawable> activeDrawable;
 	MTLRenderPassDescriptor *passDesc;
 	MTLRenderPassDescriptor *passDesc;
 
 
 	uint32 dirtyRenderState;
 	uint32 dirtyRenderState;

+ 33 - 4
src/modules/graphics/metal/Graphics.mm

@@ -155,6 +155,8 @@ Graphics::Graphics()
 	, commandBuffer(nil)
 	, commandBuffer(nil)
 	, renderEncoder(nil)
 	, renderEncoder(nil)
 	, blitEncoder(nil)
 	, blitEncoder(nil)
+	, metalLayer(nil)
+	, activeDrawable(nil)
 	, passDesc(nil)
 	, passDesc(nil)
 	, dirtyRenderState(STATEBIT_ALL)
 	, dirtyRenderState(STATEBIT_ALL)
 	, windowHasStencil(false)
 	, windowHasStencil(false)
@@ -243,9 +245,12 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 { @autoreleasepool {
 { @autoreleasepool {
 	this->width = width;
 	this->width = width;
 	this->height = height;
 	this->height = height;
+	this->metalLayer = (__bridge CAMetalLayer *) context;
 
 
 	this->windowHasStencil = windowhasstencil;
 	this->windowHasStencil = windowhasstencil;
 
 
+	metalLayer.device = device;
+
 	setViewportSize(width, height, pixelwidth, pixelheight);
 	setViewportSize(width, height, pixelwidth, pixelheight);
 
 
 	created = true;
 	created = true;
@@ -286,7 +291,7 @@ bool Graphics::setMode(void *context, int width, int height, int pixelwidth, int
 }}
 }}
 
 
 void Graphics::unSetMode()
 void Graphics::unSetMode()
-{
+{ @autoreleasepool {
 	if (!isCreated())
 	if (!isCreated())
 		return;
 		return;
 
 
@@ -300,7 +305,9 @@ void Graphics::unSetMode()
 	temporaryTextures.clear();
 	temporaryTextures.clear();
 
 
 	created = false;
 	created = false;
-}
+	metalLayer = nil;
+	activeDrawable = nil;
+}}
 
 
 void Graphics::setActive(bool enable)
 void Graphics::setActive(bool enable)
 {
 {
@@ -333,6 +340,19 @@ id<MTLRenderCommandEncoder> Graphics::useRenderEncoder()
 	if (renderEncoder == nil)
 	if (renderEncoder == nil)
 	{
 	{
 		submitBlitEncoder();
 		submitBlitEncoder();
+
+		const auto &rts = states.back().renderTargets;
+		if (rts.getFirstTarget().texture.get() != nullptr)
+		{
+
+		}
+		else
+		{
+			if (activeDrawable == nil)
+				activeDrawable = [metalLayer nextDrawable];
+			passDesc.colorAttachments[0].texture = activeDrawable.texture;
+		}
+
 		renderEncoder = [useCommandBuffer() renderCommandEncoderWithDescriptor:passDesc];
 		renderEncoder = [useCommandBuffer() renderCommandEncoderWithDescriptor:passDesc];
 		dirtyRenderState = STATEBIT_ALL;
 		dirtyRenderState = STATEBIT_ALL;
 	}
 	}
@@ -346,6 +366,8 @@ void Graphics::submitRenderEncoder()
 	{
 	{
 		[renderEncoder endEncoding];
 		[renderEncoder endEncoding];
 		renderEncoder = nil;
 		renderEncoder = nil;
+
+		passDesc.colorAttachments[0].texture = nil;
 	}
 	}
 }
 }
 
 
@@ -778,7 +800,7 @@ void Graphics::discard(const std::vector<bool> &colorbuffers, bool depthstencil)
 }
 }
 
 
 void Graphics::present(void *screenshotCallbackData)
 void Graphics::present(void *screenshotCallbackData)
-{
+{ @autoreleasepool {
 	if (!isActive())
 	if (!isActive())
 		return;
 		return;
 
 
@@ -851,12 +873,19 @@ void Graphics::present(void *screenshotCallbackData)
 		buffer->nextFrame();
 		buffer->nextFrame();
 	batchedDrawState.indexBuffer->nextFrame();
 	batchedDrawState.indexBuffer->nextFrame();
 
 
+	id<MTLCommandBuffer> cmd = getCommandBuffer();
+
+	if (cmd != nil && activeDrawable != nil)
+		[cmd presentDrawable:activeDrawable];
+
 	submitCommandBuffer();
 	submitCommandBuffer();
 
 
 	auto window = Module::getInstance<love::window::Window>(M_WINDOW);
 	auto window = Module::getInstance<love::window::Window>(M_WINDOW);
 	if (window != nullptr)
 	if (window != nullptr)
 		window->swapBuffers();
 		window->swapBuffers();
 
 
+	activeDrawable = nil;
+
 	// Reset the per-frame stat counts.
 	// Reset the per-frame stat counts.
 	drawCalls = 0;
 	drawCalls = 0;
 	//gl.stats.shaderSwitches = 0;
 	//gl.stats.shaderSwitches = 0;
@@ -875,7 +904,7 @@ void Graphics::present(void *screenshotCallbackData)
 		else
 		else
 			temporaryTextures[i].framesSinceUse++;
 			temporaryTextures[i].framesSinceUse++;
 	}
 	}
-}
+}}
 
 
 void Graphics::setColor(Colorf c)
 void Graphics::setColor(Colorf c)
 {
 {

+ 4 - 1
src/modules/graphics/metal/Metal.mm

@@ -81,7 +81,10 @@ MTLPixelFormat Metal::convertPixelFormat(PixelFormat format, bool &isSRGB)
 		break;
 		break;
 
 
 	case PIXELFORMAT_LA8_UNORM:
 	case PIXELFORMAT_LA8_UNORM:
-		mtlformat = MTLPixelFormatRGBA8Unorm;
+		// TODO: Swizzle
+		// TODO: fall back to RGBA8 when swizzle isn't available. Pixel format
+		// size calculation will need to be adjusted as well
+		mtlformat = MTLPixelFormatRG8Unorm;
 		break;
 		break;
 
 
 	case PIXELFORMAT_RGBA4_UNORM:
 	case PIXELFORMAT_RGBA4_UNORM:

+ 1 - 1
src/modules/graphics/metal/StreamBuffer.mm

@@ -51,7 +51,7 @@ public:
 		data = (uint8 *) buffer.contents;
 		data = (uint8 *) buffer.contents;
 
 
 		for (int i = 0; i < BUFFER_FRAMES; i++)
 		for (int i = 0; i < BUFFER_FRAMES; i++)
-			frameSemaphores[i] = dispatch_semaphore_create(0);
+			frameSemaphores[i] = dispatch_semaphore_create(1);
 	}}
 	}}
 
 
 	virtual ~StreamBuffer()
 	virtual ~StreamBuffer()

+ 41 - 16
src/modules/window/sdl/Window.cpp

@@ -64,7 +64,7 @@ Window::Window()
 	, mouseGrabbed(false)
 	, mouseGrabbed(false)
 	, window(nullptr)
 	, window(nullptr)
 	, glcontext(nullptr)
 	, glcontext(nullptr)
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#ifdef LOVE_GRAPHICS_METAL
 	, metalView(nullptr)
 	, metalView(nullptr)
 #endif
 #endif
 	, displayedWindowError(false)
 	, displayedWindowError(false)
@@ -292,7 +292,7 @@ std::vector<Window::ContextAttribs> Window::getContextAttribsList() const
 bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowflags, graphics::Graphics::Renderer renderer, 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)
 {
 {
 	bool needsglcontext = (windowflags & SDL_WINDOW_OPENGL) != 0;
 	bool needsglcontext = (windowflags & SDL_WINDOW_OPENGL) != 0;
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#ifdef LOVE_GRAPHICS_METAL
 	bool needsmetalview = (windowflags & SDL_WINDOW_METAL) != 0;
 	bool needsmetalview = (windowflags & SDL_WINDOW_METAL) != 0;
 #endif
 #endif
 
 
@@ -314,6 +314,14 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 			glcontext = nullptr;
 			glcontext = nullptr;
 		}
 		}
 
 
+#ifdef LOVE_GRAPHICS_METAL
+		if (metalView)
+		{
+			SDL_Metal_DestroyView(metalView);
+			metalView = nullptr;
+		}
+#endif
+
 		if (window)
 		if (window)
 		{
 		{
 			SDL_DestroyWindow(window);
 			SDL_DestroyWindow(window);
@@ -409,7 +417,7 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 			}
 			}
 		}
 		}
 	}
 	}
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#ifdef LOVE_GRAPHICS_METAL
 	else if (renderer == graphics::Graphics::RENDERER_METAL)
 	else if (renderer == graphics::Graphics::RENDERER_METAL)
 	{
 	{
 		if (create(nullptr) && window != nullptr)
 		if (create(nullptr) && window != nullptr)
@@ -430,7 +438,7 @@ bool Window::createWindowAndContext(int x, int y, int w, int h, Uint32 windowfla
 
 
 	bool failed = window == nullptr;
 	bool failed = window == nullptr;
 	failed |= (needsglcontext && !glcontext);
 	failed |= (needsglcontext && !glcontext);
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#ifdef LOVE_GRAPHICS_METAL
 	failed |= (needsmetalview && !metalView);
 	failed |= (needsmetalview && !metalView);
 #endif
 #endif
 
 
@@ -497,7 +505,7 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 	if (renderer == graphics::Graphics::RENDERER_OPENGL)
 	if (renderer == graphics::Graphics::RENDERER_OPENGL)
 		sdlflags |= SDL_WINDOW_OPENGL;
 		sdlflags |= SDL_WINDOW_OPENGL;
 
 
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#ifdef LOVE_GRAPHICS_METAL
 	if (renderer == graphics::Graphics::RENDERER_METAL)
 	if (renderer == graphics::Graphics::RENDERER_METAL)
 		sdlflags |= SDL_WINDOW_METAL;
 		sdlflags |= SDL_WINDOW_METAL;
 #endif
 #endif
@@ -587,10 +595,12 @@ bool Window::setWindow(int width, int height, WindowSettings *settings)
 		double scaledw, scaledh;
 		double scaledw, scaledh;
 		fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
 		fromPixels((double) pixelWidth, (double) pixelHeight, scaledw, scaledh);
 
 
-		void *context = (void *) glcontext;
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
-		if (renderer == graphics::Graphics::RENDERER_METAL)
-			context = (void *) metalView;
+		void *context = nullptr;
+		if (renderer == graphics::Graphics::RENDERER_OPENGL)
+			context = (void *) glcontext;
+#ifdef LOVE_GRAPHICS_METAL
+		if (renderer == graphics::Graphics::RENDERER_METAL && metalView)
+			context = (void *) SDL_Metal_GetLayer(metalView);
 #endif
 #endif
 
 
 		graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil);
 		graphics->setMode(context, (int) scaledw, (int) scaledh, pixelWidth, pixelHeight, f.stencil);
@@ -629,7 +639,16 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
 
 
 	// Set the new display mode as the current display mode.
 	// Set the new display mode as the current display mode.
 	SDL_GetWindowSize(window, &windowWidth, &windowHeight);
 	SDL_GetWindowSize(window, &windowWidth, &windowHeight);
-	SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
+
+	pixelWidth = windowWidth;
+	pixelHeight = windowHeight;
+
+	if ((wflags & SDL_WINDOW_OPENGL) != 0)
+		SDL_GL_GetDrawableSize(window, &pixelWidth, &pixelHeight);
+#ifdef LOVE_GRAPHICS_METAL
+	else if ((wflags & SDL_WINDOW_METAL) != 0)
+		SDL_Metal_GetDrawableSize(window, &pixelWidth, &pixelHeight);
+#endif
 
 
 	if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
 	if ((wflags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP)
 	{
 	{
@@ -673,8 +692,12 @@ void Window::updateSettings(const WindowSettings &newsettings, bool updateGraphi
 	// Verify MSAA setting.
 	// Verify MSAA setting.
 	int buffers = 0;
 	int buffers = 0;
 	int samples = 0;
 	int samples = 0;
-	SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
-	SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
+
+	if ((wflags & SDL_WINDOW_OPENGL) != 0)
+	{
+		SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
+		SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &samples);
+	}
 
 
 	settings.msaa = (buffers > 0 ? samples : 0);
 	settings.msaa = (buffers > 0 ? samples : 0);
 	settings.vsync = getVSync();
 	settings.vsync = getVSync();
@@ -729,7 +752,7 @@ void Window::close(bool allowExceptions)
 		glcontext = nullptr;
 		glcontext = nullptr;
 	}
 	}
 
 
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#ifdef LOVE_GRAPHICS_METAL
 	if (metalView)
 	if (metalView)
 	{
 	{
 		SDL_Metal_DestroyView(metalView);
 		SDL_Metal_DestroyView(metalView);
@@ -787,10 +810,11 @@ bool Window::setFullscreen(bool fullscreen, Window::FullscreenType fstype)
 
 
 	if (SDL_SetWindowFullscreen(window, sdlflags) == 0)
 	if (SDL_SetWindowFullscreen(window, sdlflags) == 0)
 	{
 	{
-		SDL_GL_MakeCurrent(window, glcontext);
+		if (glcontext)
+			SDL_GL_MakeCurrent(window, glcontext);
 		updateSettings(newsettings, true);
 		updateSettings(newsettings, true);
 
 
-		// Apparently this gets un-set when we exit fullscreen (at least in OS X).
+		// Apparently this gets un-set when we exit fullscreen (at least in macOS).
 		if (!fullscreen)
 		if (!fullscreen)
 			SDL_SetWindowMinimumSize(window, settings.minwidth, settings.minheight);
 			SDL_SetWindowMinimumSize(window, settings.minwidth, settings.minheight);
 
 
@@ -1086,7 +1110,8 @@ bool Window::isMinimized() const
 
 
 void Window::swapBuffers()
 void Window::swapBuffers()
 {
 {
-	SDL_GL_SwapWindow(window);
+	if (glcontext)
+		SDL_GL_SwapWindow(window);
 }
 }
 
 
 bool Window::hasFocus() const
 bool Window::hasFocus() const

+ 1 - 1
src/modules/window/sdl/Window.h

@@ -168,7 +168,7 @@ private:
 	SDL_Window *window;
 	SDL_Window *window;
 
 
 	SDL_GLContext glcontext;
 	SDL_GLContext glcontext;
-#if defined(LOVE_MACOS) || defined(LOVE_IOS)
+#ifdef LOVE_GRAPHICS_METAL
 	SDL_MetalView metalView;
 	SDL_MetalView metalView;
 #endif
 #endif