Sfoglia il codice sorgente

WIP: macOS port
- Work on macOS OpenGL render API

Marko Pintera 8 anni fa
parent
commit
0f55a14b92

+ 15 - 1
Source/BansheeCore/Private/MacOS/BsMacOSPlatform.h

@@ -3,10 +3,24 @@
 #pragma once
 
 #include "Platform/BsPlatform.h"
-#import <Cocoa/Cocoa.h>
+
+// Don't include macOS frameworks when generating script bindings, as it can't find them
+#ifndef BS_SBGEN
+#include <Cocoa/Cocoa.h>
+#endif
 
 namespace bs
 {
+	// Forward declare Cocoa types for SBGen purposes, since we didn't include Cocoa.h above
+#if BS_SBGEN
+	class NSImage;
+	class NSCursor;
+	class NSScreen;
+	class NSWindow;
+	struct NSRect;
+	struct NSPoint;
+#endif
+
 	class CocoaWindow;
 
 	/** @addtogroup Platform-Internal

+ 1 - 1
Source/BansheeCore/Private/MacOS/BsMacOSPlatform.mm

@@ -10,7 +10,7 @@
 #include "Private/MacOS/BsMacOSDropTarget.h"
 #include "String/BsUnicode.h"
 #include "BsCoreApplication.h"
-#include <atomic>
+#import <Cocoa/Cocoa.h>
 #import <Carbon/Carbon.h>
 
 /** Application implementation that overrides the terminate logic with custom shutdown, and tracks Esc key presses. */

+ 10 - 2
Source/BansheeCore/Private/MacOS/BsMacOSWindow.h

@@ -68,8 +68,13 @@ namespace bs
 		CocoaWindow(const WINDOW_DESC& desc);
 		~CocoaWindow();
 
-		/**	Returns the current area of the window, relative to the screen. */
-		Rect2I getArea() const;
+		/**
+		 * Returns the current area of the window, relative to the screen.
+		 *
+		 * @param[in] topLeftOrigin		If true the coordinates will use a top-left origin coordinate system (engine native).
+		 * 								If false the coordinates will use a bottom-left origin (Cocoa native).
+		 */
+		Rect2I getArea(bool topLeftOrigin = true) const;
 
 		/** Hides the window. */
 		void hide();
@@ -143,6 +148,9 @@ namespace bs
 		 */
 		void _unregisterForDragAndDrop();
 
+		/** Returns the area of the screen that the window currently occupies, in Cocoa screen coordinates. */
+		Rect2I _getScreenArea() const;
+
 		/** Returns internal private data for use by friends. */
 		Pimpl* _getPrivateData() const { return m; }
 

+ 16 - 2
Source/BansheeCore/Private/MacOS/BsMacOSWindow.mm

@@ -675,12 +675,13 @@ namespace bs
 		m->window = nil;
 	}
 
-	Rect2I CocoaWindow::getArea() const
+	Rect2I CocoaWindow::getArea(bool topLeftOrigin) const
 	{ @autoreleasepool {
 		NSRect frameRect = [m->window frame];
 		NSRect contentRect = [m->window contentRectForFrameRect:frameRect];
 
-		flipY([m->window screen], contentRect);
+		if(topLeftOrigin)
+			flipY([m->window screen], contentRect);
 
 		return Rect2I(
 			(INT32)contentRect.origin.x,
@@ -809,4 +810,17 @@ namespace bs
 		if(m->numDropTargets == 0)
 			[m->window unregisterDraggedTypes];
 	}
+
+	Rect2I CocoaWindow::_getScreenArea() const
+	{
+		NSRect screenRect = [[m->window screen] frame];
+
+		Rect2I area;
+		area.x = (INT32)screenRect.origin.x;
+		area.y = (INT32)screenRect.origin.y;
+		area.width = (UINT32)screenRect.size.width;
+		area.height = (UINT32)screenRect.size.height;
+
+		return area;
+	}
 }

+ 5 - 0
Source/BansheeGLRenderAPI/BsGLPrerequisites.h

@@ -36,7 +36,12 @@
 #   include <GL/glu.h>
 #   define GL_GLEXT_PROTOTYPES
 #elif BS_PLATFORM == BS_PLATFORM_OSX
+<<<<<<< HEAD
 #   include <GL/glew.h>
+=======
+#	include <OpenGL/gl3.h>
+#	include <OpenGL/gl3ext.h>
+>>>>>>> WIP: macOS port
 #endif
 
 #if BS_THREAD_SUPPORT == 1

+ 18 - 0
Source/BansheeGLRenderAPI/BsGLUtil.h

@@ -41,4 +41,22 @@ namespace bs { namespace ct
 		/** @} */
 	}}
 
+#elif BS_PLATFORM == BS_PLATFORM_OSX
+
+#include "MacOS/BsMacOSGLSupport.h"
+
+namespace bs::ct
+{
+	/** @addtogroup GL
+	 *  @{
+	 */
+
+	/**	Helper method that returns a platform specific GL support object. */
+	GLSupport* getGLSupport()
+	{
+		return bs_new<MacOSGLSupport>();
+	}
+
+	/** @} */
+}
 #endif

+ 5 - 2
Source/BansheeGLRenderAPI/CMakeLists.txt

@@ -15,7 +15,7 @@ set(BansheeGLRenderAPI_INC
 
 if(WIN32)
 	set(BansheeGLRenderAPI_INC ${BansheeGLRenderAPI_INC} "Source/Win32")
-else()
+elseif(LINUX)
 	set(BansheeGLRenderAPI_INC ${BansheeGLRenderAPI_INC} ${OPENGL_INCLUDE_DIR})
 endif()
 	
@@ -31,9 +31,12 @@ target_compile_definitions(BansheeGLRenderAPI PRIVATE -DBS_RSGL_EXPORTS -DGLEW_S
 if(WIN32)
 	## External lib: OpenGL
 	target_link_libraries(BansheeGLRenderAPI PRIVATE glu32 opengl32)
-else()
+elseif(LINUX)
 	## External lib: OpenGL
 	target_link_libraries(BansheeGLRenderAPI PRIVATE ${OPENGL_LIBRARIES})
+elseif(APPLE) # MacOS
+	target_link_framework(BansheeGLRenderAPI Foundation)
+	target_link_framework(BansheeGLRenderAPI CoreGraphics)
 endif()
 
 ## Local libs

+ 23 - 1
Source/BansheeGLRenderAPI/CMakeSources.cmake

@@ -43,7 +43,6 @@ set(BS_BANSHEEGLRENDERAPI_SRC_WIN32
 )
 
 set(BS_BANSHEEGLRENDERAPI_SRC_NOFILTER
-	"glew.cpp"
 	"BsGLVertexArrayObjectManager.cpp"
 	"BsGLVertexBuffer.cpp"
 	"BsGLTimerQuery.cpp"
@@ -72,6 +71,10 @@ set(BS_BANSHEEGLRENDERAPI_SRC_NOFILTER
 	"BsGLTextureView.cpp"
 )
 
+if(NOT APPLE)
+	list(APPEND BS_BANSHEEGLRENDERAPI_SRC_NOFILTER "glew.cpp")
+endif()
+
 set(BS_BANSHEEGLRENDERAPI_INC_GLSL
 	"GLSL/BsGLSLProgramPipelineManager.h"
 	"GLSL/BsGLSLProgramFactory.h"
@@ -101,6 +104,20 @@ set(BS_BANSHEEGLRENDERAPI_SRC_LINUX
 	"Linux/BsLinuxVideoModeInfo.cpp"
 )
 
+set(BS_BANSHEEGLRENDERAPI_INC_MACOS
+	"MacOS/BsMacOSContext.h"
+	"MacOS/BsMacOSGLSupport.h"
+	"MacOS/BsMacOSRenderWindow.h"
+	"MacOS/BsMacOSVideoModeInfo.h"
+)
+
+set(BS_BANSHEEGLRENDERAPI_SRC_MACOS
+	"MacOS/BsMacOSContext.mm"
+	"MacOS/BsMacOSGLSupport.cpp"
+	"MacOS/BsMacOSRenderWindow.mm"
+	"MacOS/BsMacOSVideoModeInfo.cpp"
+)
+
 source_group("Source Files\\GLSL" FILES ${BS_BANSHEEGLRENDERAPI_SRC_GLSL})
 source_group("Header Files" FILES ${BS_BANSHEEGLRENDERAPI_INC_NOFILTER})
 source_group("Source Files\\Win32" FILES ${BS_BANSHEEGLRENDERAPI_SRC_WIN32})
@@ -125,4 +142,9 @@ elseif(LINUX)
 		${BS_BANSHEEGLRENDERAPI_INC_LINUX}
 		${BS_BANSHEEGLRENDERAPI_SRC_LINUX}
 	)
+elseif(APPLE)
+	list(APPEND BS_BANSHEEGLRENDERAPI_SRC
+		${BS_BANSHEEGLRENDERAPI_INC_MACOS}
+		${BS_BANSHEEGLRENDERAPI_SRC_MACOS}
+	)
 endif()

+ 3 - 3
Source/BansheeGLRenderAPI/Linux/BsLinuxVideoModeInfo.h

@@ -1,12 +1,12 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+//**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
 #pragma once
 
 #include "BsGLPrerequisites.h"
 #include "RenderAPI/BsVideoModeInfo.h"
 #include <X11/extensions/Xrandr.h>
 
-namespace bs { namespace ct
+namespace bs::ct
 {
 	/** @addtogroup GL
 	 *  @{
@@ -53,5 +53,5 @@ namespace bs { namespace ct
 	};
 
 	/** @} */
-}}
+}
 

+ 55 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSContext.h

@@ -0,0 +1,55 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsGLPrerequisites.h"
+#include "BsGLContext.h"
+
+namespace bs::ct
+{
+	/** @addtogroup GL
+	 *  @{
+	 */
+
+	/**	MacOS specific implementation of an OpenGL context. */
+	class MacOSContext : public GLContext
+	{
+		struct Pimpl;
+	public:
+		/**
+		 * Constructs a new OpenGL context.
+		 *
+		 * @param[in]	depthStencil	True if the framebuffer using the context can have a depth-stencil buffer.
+		 * @param[in]	msaaCount		Number of samples the framebuffer using the context is allowed to have.
+		 */
+		MacOSContext(bool depthStencil, UINT32 msaaCount);
+		virtual ~MacOSContext();
+
+		/** @copydoc GLContext::setCurrent */
+		void setCurrent(const RenderWindow& window) override;
+
+		/** @copydoc GLContext::endCurrent */
+		void endCurrent() override;
+
+		/** @copydoc GLContext::releaseContext  */
+		void releaseContext() override;
+
+		/** Marks the context as dirty and requiring update. Should be called when the drawable changes size or location. */
+		void markAsDirty();
+
+		/** Updates the context if dirty. */
+		void updateIfDirty();
+
+		/** Enables or disables VSync using the specified interval. Interval of 0 disables VSync. */
+		void setVSync(int interval);
+
+		/** Swaps the framebuffer currently attached to this context. */
+		void swapBuffers();
+
+	private:
+		Pimpl* m;
+	};
+
+	/** @} */
+}
+

+ 123 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSContext.mm

@@ -0,0 +1,123 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "MacOS/BsMacOSContext.h"
+#include "MacOS/BsMacOSGLSupport.h"
+#import <AppKit/AppKit.h>
+
+namespace bs::ct
+{
+	struct MacOSContext::Pimpl
+	{
+		NSOpenGLContext* context = nil;
+		bool dirty = true;
+	};
+
+	MacOSContext::MacOSContext(bool depthStencil, UINT32 msaaCount)
+	{ @autoreleasepool {
+		NSOpenGLPixelFormatAttribute attributes[] =
+		{
+			NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core,
+			NSOpenGLPFADoubleBuffer,
+			NSOpenGLPFAAccelerated,
+			NSOpenGLPFADepthSize, depthStencil ? 24U : 0U,
+			NSOpenGLPFAStencilSize, depthStencil ? 8U : 0U,
+			NSOpenGLPFASampleBuffers, msaaCount > 1 ? 1U : 0U,
+		};
+
+		UINT32 attrIdx = 0;
+
+		attributes[attrIdx++] = NSOpenGLPFAOpenGLProfile;
+		attributes[attrIdx++] = NSOpenGLProfileVersion4_1Core;
+
+		attributes[attrIdx++] = NSOpenGLPFADoubleBuffer;
+		attributes[attrIdx++] = NSOpenGLPFAAccelerated;
+
+		attributes[attrIdx++] = NSOpenGLPFAColorSize;
+		attributes[attrIdx++] = 32;
+
+		if(depthStencil)
+		{
+			attributes[attrIdx++] = NSOpenGLPFADepthSize;
+			attributes[attrIdx++] = 24;
+			attributes[attrIdx++] = NSOpenGLPFAStencilSize;
+			attributes[attrIdx++] = 8;
+		}
+
+		if(msaaCount > 1)
+		{
+			attributes[attrIdx++] = NSOpenGLPFAMultisample;
+			attributes[attrIdx++] = NSOpenGLPFASampleBuffers;
+			attributes[attrIdx++] = 1;
+			attributes[attrIdx++] = NSOpenGLPFASamples;
+			attributes[attrIdx++] = msaaCount;
+			attributes[attrIdx++] = NSOpenGLPFANoRecovery;
+		}
+
+		attributes[attrIdx] = 0;
+
+		NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
+		m = bs_new<Pimpl>();
+		m->context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
+
+		markAsDirty();
+	}}
+
+	MacOSContext::~MacOSContext()
+	{
+		bs_delete(m);
+	}
+
+	void MacOSContext::setCurrent(const RenderWindow& renderWindow)
+	{
+		NSWindow* window;
+		renderWindow.getCustomAttribute("WINDOW", &window);
+
+		[m->context setView:[window contentView]];
+		[m->context makeCurrentContext];
+		[m->context update];
+
+		m->dirty = false;
+	}
+
+	void MacOSContext::endCurrent()
+	{
+		[m->context setView:nil];
+		[m->context clearDrawable];
+		[NSOpenGLContext clearCurrentContext];
+	}
+
+	void MacOSContext::releaseContext()
+	{
+		m->context = nil;
+	}
+
+	void MacOSContext::markAsDirty()
+	{
+		m->dirty = true;
+	}
+
+	void MacOSContext::updateIfDirty()
+	{
+		if(m->dirty)
+		{
+			[m->context update];
+			m->dirty = false;
+		}
+	}
+
+	void MacOSContext::setVSync(int interval)
+	{
+		if(interval < 0)
+			interval = 0;
+
+		[m->context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
+	}
+
+	void MacOSContext::swapBuffers()
+	{
+		[m->context flushBuffer];
+
+		updateIfDirty();
+	}
+}
+

+ 70 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSGLSupport.cpp

@@ -0,0 +1,70 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "MacOS/BsMacOSGLSupport.h"
+#include "MacOS/BsMacOSContext.h"
+#include "MacOS/BsMacOSRenderWindow.h"
+#include "BsMacOSVideoModeInfo.h"
+#include "BsGLRenderAPI.h"
+#include <dlfcn.h>
+
+namespace bs::ct
+{
+	SPtr<bs::RenderWindow> MacOSGLSupport::newWindow(
+		RENDER_WINDOW_DESC& desc,
+		UINT32 windowId,
+		SPtr<bs::RenderWindow> parentWindow)
+	{
+		bs::MacOSRenderWindow* window = new (bs_alloc<bs::MacOSRenderWindow>()) bs::MacOSRenderWindow(desc, windowId, *this);
+		return SPtr<bs::RenderWindow>(window, &bs::CoreObject::_delete<bs::MacOSRenderWindow, GenAlloc>);
+	}
+
+	SPtr<RenderWindow> MacOSGLSupport::newWindowCore(RENDER_WINDOW_DESC& desc, UINT32 windowId)
+	{
+		MacOSRenderWindow* window = new (bs_alloc<MacOSRenderWindow>()) MacOSRenderWindow(desc, windowId, *this);
+
+		return bs_shared_ptr<MacOSRenderWindow>(window);
+	}
+
+	void MacOSGLSupport::start()
+	{
+		// Do nothing
+	}
+
+	void MacOSGLSupport::stop()
+	{
+		// Do nothing
+	}
+
+	SPtr<MacOSContext> MacOSGLSupport::createContext(bool depthStencil, UINT32 msaaCount)
+	{
+		GLRenderAPI* rapi = static_cast<GLRenderAPI*>(RenderAPI::instancePtr());
+
+		// If RenderAPI has initialized a context use that, otherwise we create our own
+		if (!rapi->_isContextInitialized())
+			return bs_shared_ptr_new<MacOSContext>(depthStencil, msaaCount);
+		else
+		{
+			SPtr<GLContext> context = rapi->_getMainContext();
+			return std::static_pointer_cast<MacOSContext>(context);
+		}
+	}
+
+	void* MacOSGLSupport::getProcAddress(const String& procname)
+	{
+		static void* image = nullptr;
+
+		if (!image)
+			image = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_LAZY);
+
+		if(!image)
+			return nullptr;
+
+		return dlsym(image, (const char*)procname.c_str());
+	}
+
+	SPtr<VideoModeInfo> MacOSGLSupport::getVideoModeInfo() const
+	{
+		return bs_shared_ptr_new<MacOSVideoModeInfo>();
+	}
+}
+

+ 45 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSGLSupport.h

@@ -0,0 +1,45 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsGLPrerequisites.h"
+#include "BsGLSupport.h"
+#include "BsGLRenderAPI.h"
+
+namespace bs::ct
+{
+	class MacOSContext;
+
+	/** @addtogroup GL
+	 *  @{
+	 */
+
+	/**	Handles OpenGL initialization, window creation and extensions on MacOS. */
+	class MacOSGLSupport : public GLSupport
+	{
+	public:
+		/** @copydoc GLSupport::newWindow */
+		SPtr<bs::RenderWindow> newWindow(RENDER_WINDOW_DESC& desc, UINT32 windowId, SPtr<bs::RenderWindow> parentWindow) override;
+
+		/** @copydoc GLSupport::newWindowCore */
+		SPtr<RenderWindow> newWindowCore(RENDER_WINDOW_DESC& desc, UINT32 windowId) override;
+
+		/** @copydoc GLSupport::start */
+		void start() override;
+
+		/** @copydoc GLSupport::stop */
+		void stop() override;
+
+		/** @copydoc GLSupport::getProcAddress */
+		void* getProcAddress(const String& procname) override;
+
+		/** Creates a new OpenGL context. */
+		SPtr<MacOSContext> createContext(bool depthStencil, UINT32 msaaCount);
+
+		/** @copydoc GLSupport::getVideoModeInfo */
+		SPtr<VideoModeInfo> getVideoModeInfo() const override;
+	};
+
+	/** @} */
+}
+

+ 167 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSRenderWindow.h

@@ -0,0 +1,167 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "RenderAPI/BsRenderWindow.h"
+
+namespace bs
+{
+	class CocoaWindow;
+	class MacOSRenderWindow;
+
+	namespace ct
+	{
+		class MacOSGLSupport;
+		class MacOSContext;
+		class MacOSRenderWindow;
+	}
+
+	/** @addtogroup GL
+	 *  @{
+	 */
+
+	/**
+	 * Render window implementation for MacOS.
+	 *
+	 * @note	Sim thread only.
+	 */
+	class MacOSRenderWindow : public RenderWindow
+	{
+	public:
+		~MacOSRenderWindow() { }
+
+		/** @copydoc RenderWindow::getCustomAttribute */
+		void getCustomAttribute(const String& name, void* pData) const override;
+
+		/** @copydoc RenderWindow::screenToWindowPos */
+		Vector2I screenToWindowPos(const Vector2I& screenPos) const override;
+
+		/** @copydoc RenderWindow::windowToScreenPos */
+		Vector2I windowToScreenPos(const Vector2I& windowPos) const override;
+
+		/** @copydoc RenderWindow::getCore */
+		SPtr<ct::MacOSRenderWindow> getCore() const;
+
+	protected:
+		friend class GLRenderWindowManager;
+		friend class ct::MacOSGLSupport;
+		friend class ct::MacOSRenderWindow;
+
+		MacOSRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, ct::MacOSGLSupport& glSupport);
+
+		/** @copydoc RenderWindow::getProperties */
+		const RenderTargetProperties& getPropertiesInternal() const override { return mProperties; }
+
+		/** @copydoc RenderWindow::syncProperties */
+		void syncProperties() override;
+
+	private:
+		RenderWindowProperties mProperties;
+		const ct::MacOSGLSupport& mGLSupport;
+	};
+
+	namespace ct
+	{
+		/**
+		 * Render window implementation for MacOS.
+		 *
+		 * @note	Core thread only.
+		 */
+		class MacOSRenderWindow : public RenderWindow
+		{
+		public:
+			MacOSRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, MacOSGLSupport& glSupport);
+			~MacOSRenderWindow();
+
+			/** @copydoc RenderWindow::setFullscreen(UINT32, UINT32, float, UINT32) */
+			void setFullscreen(UINT32 width, UINT32 height, float refreshRate = 60.0f, UINT32 monitorIdx = 0) override;
+
+			/** @copydoc RenderWindow::setFullscreen(const VideoMode&) */
+			void setFullscreen(const VideoMode& videoMode) override;
+
+			/** @copydoc RenderWindow::setWindowed */
+			void setWindowed(UINT32 width, UINT32 height) override;
+
+			/** @copydoc RenderWindow::setHidden */
+			void setHidden(bool hidden) override;
+
+			/** @copydoc RenderWindow::minimize */
+			void minimize() override;
+
+			/** @copydoc RenderWindow::maximize */
+			void maximize() override;
+
+			/** @copydoc RenderWindow::restore */
+			void restore() override;
+
+			/** @copydoc RenderWindow::move */
+			void move(INT32 left, INT32 top) override;
+
+			/** @copydoc RenderWindow::resize */
+			void resize(UINT32 width, UINT32 height) override;
+
+			/** @copydoc RenderWindow::setVSync */
+			void setVSync(bool enabled, UINT32 interval = 1) override;
+
+			/**
+			 * Copies the contents of a frame buffer into the pre-allocated buffer.
+			 *
+			 * @param[out]	dst		Previously allocated buffer to read the contents into. Must be of valid size.
+			 * @param[in]	buffer	Frame buffer to read the contents from.
+			 */
+			void copyToMemory(PixelData& dst, FrameBuffer buffer);
+
+			/** @copydoc RenderWindow::swapBuffers */
+			void swapBuffers(UINT32 syncMask) override;
+
+			/** @copydoc RenderWindow::getCustomAttribute */
+			void getCustomAttribute(const String& name, void* pData) const override;
+
+			/** @copydoc RenderWindow::setActive */
+			void setActive(bool state) override;
+
+			/** @copydoc RenderWindow::_windowMovedOrResized */
+			void _windowMovedOrResized() override;
+
+			/** Returns a lock that can be used for accessing synced properties. */
+			SpinLock& _getPropertiesLock() { return mLock;}
+
+		protected:
+			friend class MacOSGLSupport;
+
+			/** @copydoc CoreObject::initialize */
+			void initialize() override;
+
+			/** @copydoc RenderWindow::getProperties */
+			const RenderTargetProperties& getPropertiesInternal() const override { return mProperties; }
+
+			/** @copydoc RenderWindow::getSyncedProperties */
+			RenderWindowProperties& getSyncedProperties() override { return mSyncedProperties; }
+
+			/** @copydoc RenderWindow::syncProperties */
+			void syncProperties() override;
+
+			/** Changes the display mode (resolution, refresh rate) of the specified output device. */
+			void setDisplayMode(const VideoOutputInfo& output, const VideoMode& mode);
+
+		protected:
+			friend class bs::MacOSRenderWindow;
+
+			CocoaWindow* mWindow;
+			SPtr<MacOSContext> mContext;
+			ct::MacOSGLSupport& mGLSupport;
+			RenderWindowProperties mProperties;
+			RenderWindowProperties mSyncedProperties;
+			bool mIsChild;
+			bool mShowOnSwap;
+
+			// Coordinate conversions
+			Mutex mDimensionsMutex;
+			Rect2I mScreenArea;
+			Rect2I mNativeWindowArea;
+		};
+	}
+
+	/** @} */
+}
+

+ 537 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSRenderWindow.mm

@@ -0,0 +1,537 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
+#define BS_COCOA_INTERNALS
+#include "MacOS/BsMacOSWindow.h"
+#include "Math/BsMath.h"
+#include "CoreThread/BsCoreThread.h"
+#include "MacOS/BsMacOSRenderWindow.h"
+#include "MacOS/BsMacOSWindow.h"
+#include "MacOS/BsMacOSVideoModeInfo.h"
+#include "MacOS/BsMacOSGLSupport.h"
+#include "MacOS/BsMacOSContext.h"
+#include "BsGLRenderWindowManager.h"
+#include "BsGLPixelFormat.h"
+
+namespace bs
+{
+	MacOSRenderWindow::MacOSRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, ct::MacOSGLSupport& glSupport)
+			:RenderWindow(desc, windowId), mProperties(desc), mGLSupport(glSupport)
+	{ }
+
+	void MacOSRenderWindow::getCustomAttribute(const String& name, void* data) const
+	{
+		if(name == "WINDOW" || name == "COCOA_WINDOW")
+		{
+			blockUntilCoreInitialized();
+			getCore()->getCustomAttribute(name, data);
+			return;
+		}
+	}
+
+	Vector2I MacOSRenderWindow::screenToWindowPos(const Vector2I& screenPos) const
+	{
+		blockUntilCoreInitialized();
+
+		Vector2I point;
+		point.x = screenPos.x - mProperties.left;
+		point.y = screenPos.y - mProperties.top;
+
+		return point;
+	}
+
+	Vector2I MacOSRenderWindow::windowToScreenPos(const Vector2I& windowPos) const
+	{
+		blockUntilCoreInitialized();
+
+		Vector2I point;
+		point.x = windowPos.x + mProperties.left;
+		point.y = windowPos.y + mProperties.top;
+
+		return point;
+	}
+
+	SPtr<ct::MacOSRenderWindow> MacOSRenderWindow::getCore() const
+	{
+		return std::static_pointer_cast<ct::MacOSRenderWindow>(mCoreSpecific);
+	}
+
+	void MacOSRenderWindow::syncProperties()
+	{
+		ScopedSpinLock lock(getCore()->_getPropertiesLock());
+		mProperties = getCore()->mSyncedProperties;
+	}
+
+	namespace ct
+	{
+		MacOSRenderWindow::MacOSRenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, MacOSGLSupport& glsupport)
+			: RenderWindow(desc, windowId), mWindow(nullptr), mGLSupport(glsupport), mContext(nullptr), mProperties(desc)
+			, mSyncedProperties(desc), mIsChild(false), mShowOnSwap(false)
+		{ }
+
+		MacOSRenderWindow::~MacOSRenderWindow()
+		{
+			// Make sure to set the original desktop video mode before we exit
+			if(mProperties.isFullScreen)
+				setWindowed(50, 50);
+
+			if (mWindow != nullptr)
+			{
+				bs_delete(mWindow);
+				mWindow = nullptr;
+			}
+		}
+
+		void MacOSRenderWindow::initialize()
+		{
+			RenderWindowProperties& props = mProperties;
+
+			props.isFullScreen = mDesc.fullscreen;
+			mIsChild = false;
+
+			WINDOW_DESC windowDesc;
+			windowDesc.x = mDesc.left;
+			windowDesc.y = mDesc.top;
+			windowDesc.width = mDesc.videoMode.getWidth();
+			windowDesc.height = mDesc.videoMode.getHeight();
+			windowDesc.title = mDesc.title;
+			windowDesc.showDecorations = mDesc.showTitleBar;
+			windowDesc.allowResize = mDesc.allowResize;
+			windowDesc.modal = mDesc.modal;
+
+			auto iter = mDesc.platformSpecific.find("parentWindowHandle");
+			mIsChild = iter != mDesc.platformSpecific.end();
+
+			props.isFullScreen = mDesc.fullscreen && !mIsChild;
+
+			mShowOnSwap = mDesc.hideUntilSwap;
+			props.isHidden = mDesc.hideUntilSwap || mDesc.hidden;
+
+			mWindow = bs_new<CocoaWindow>(windowDesc);
+			mWindow->_setUserData(this);
+
+			Rect2I area = mWindow->getArea();
+			props.width = area.width;
+			props.height = area.height;
+			props.top = area.y;
+			props.left = area.x;
+
+			props.hwGamma = mDesc.gamma;
+			props.multisampleCount = mDesc.multisampleCount;
+
+			mContext = mGLSupport.createContext(mDesc.depthBuffer, mDesc.multisampleCount);
+
+			if(mDesc.fullscreen && !mIsChild)
+				setFullscreen(mDesc.videoMode);
+
+			if(mDesc.vsync && mDesc.vsyncInterval > 0)
+				setVSync(true, mDesc.vsyncInterval);
+
+			mScreenArea = mWindow->_getScreenArea();
+			mNativeWindowArea = mWindow->_getScreenArea();
+
+			{
+				ScopedSpinLock lock(mLock);
+				mSyncedProperties = props;
+			}
+
+			bs::RenderWindowManager::instance().notifySyncDataDirty(this);
+			RenderWindow::initialize();
+		}
+
+		void MacOSRenderWindow::setFullscreen(UINT32 width, UINT32 height, float refreshRate, UINT32 monitorIdx)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			VideoMode videoMode(width, height, refreshRate, monitorIdx);
+			setFullscreen(videoMode);
+		}
+
+		void MacOSRenderWindow::setFullscreen(const VideoMode& mode)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			if (mIsChild)
+				return;
+
+			const VideoModeInfo& videoModeInfo = RenderAPI::instance().getVideoModeInfo();
+
+			UINT32 outputIdx = mode.getOutputIdx();
+			if(outputIdx >= videoModeInfo.getNumOutputs())
+			{
+				LOGERR("Invalid output device index.")
+				return;
+			}
+
+			const VideoOutputInfo& outputInfo = videoModeInfo.getOutputInfo (outputIdx);
+
+			if(!mode.isCustom())
+				setDisplayMode(outputInfo, mode);
+			else
+			{
+				// Look for mode matching the requested resolution
+				UINT32 foundMode = (UINT32)-1;
+				UINT32 numModes = outputInfo.getNumVideoModes();
+				for (UINT32 i = 0; i < numModes; i++)
+				{
+					const VideoMode& currentMode = outputInfo.getVideoMode(i);
+
+					if (currentMode.getWidth() == mode.getWidth() && currentMode.getHeight() == mode.getHeight())
+					{
+						foundMode = i;
+
+						if (Math::approxEquals(currentMode.getRefreshRate(), mode.getRefreshRate()))
+							break;
+					}
+				}
+
+				if (foundMode == (UINT32)-1)
+				{
+					LOGERR("Unable to enter fullscreen, unsupported video mode requested.");
+					return;
+				}
+
+				setDisplayMode(outputInfo, outputInfo.getVideoMode(foundMode));
+			}
+
+			mWindow->setFullscreen();
+
+			RenderWindowProperties& props = mProperties;
+			props.isFullScreen = true;
+
+			props.top = 0;
+			props.left = 0;
+			props.width = mode.getWidth();
+			props.height = mode.getHeight();
+
+			_windowMovedOrResized();
+		}
+
+		void MacOSRenderWindow::setWindowed(UINT32 width, UINT32 height)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			RenderWindowProperties& props = mProperties;
+
+			if (!props.isFullScreen)
+				return;
+
+			// Restore original display mode
+			const VideoModeInfo& videoModeInfo = RenderAPI::instance().getVideoModeInfo();
+
+			UINT32 outputIdx = 0; // 0 is always primary
+			if(outputIdx >= videoModeInfo.getNumOutputs())
+			{
+				LOGERR("Invalid output device index.")
+				return;
+			}
+
+			const VideoOutputInfo& outputInfo = videoModeInfo.getOutputInfo(outputIdx);
+			setDisplayMode(outputInfo, outputInfo.getDesktopVideoMode());
+
+			mWindow->setWindowed();
+
+			props.isFullScreen = false;
+			props.width = width;
+			props.height = height;
+
+			{
+				ScopedSpinLock lock(mLock);
+				mSyncedProperties.width = props.width;
+				mSyncedProperties.height = props.height;
+			}
+
+			bs::RenderWindowManager::instance().notifySyncDataDirty(this);
+			_windowMovedOrResized();
+		}
+
+		void MacOSRenderWindow::setDisplayMode(const VideoOutputInfo& output, const VideoMode& mode)
+		{
+			CGDisplayFadeReservationToken fadeToken = kCGDisplayFadeReservationInvalidToken;
+			if (CGAcquireDisplayFadeReservation(5.0f, &fadeToken))
+				CGDisplayFade(fadeToken, 0.3f, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, TRUE);
+
+			auto& destOutput = static_cast<const MacOSVideoOutputInfo&>(output);
+			auto& newMode = static_cast<const MacOSVideoMode&>(mode);
+			auto& desktopMode = static_cast<const MacOSVideoMode&>(output.getDesktopVideoMode());
+
+			CGDirectDisplayID displayID = destOutput._getDisplayID();
+
+			if (desktopMode._getModeRef() == newMode._getModeRef())
+			{
+				CGDisplaySetDisplayMode(displayID, newMode._getModeRef(), nullptr);
+
+				if (CGDisplayIsMain(displayID))
+					CGReleaseAllDisplays();
+				else
+					CGDisplayRelease(displayID);
+			}
+			else
+			{
+				CGError status;
+				if (CGDisplayIsMain(displayID))
+					status = CGCaptureAllDisplays();
+				else
+					status = CGDisplayCapture(displayID);
+
+				if (status != kCGErrorSuccess)
+					goto UNFADE;
+
+				status = CGDisplaySetDisplayMode(displayID, newMode._getModeRef(), nullptr);
+				if(status != kCGErrorSuccess)
+				{
+					if (CGDisplayIsMain(displayID))
+						CGReleaseAllDisplays();
+					else
+						CGDisplayRelease(displayID);
+
+					goto UNFADE;
+				}
+			}
+
+			UNFADE:
+			if (fadeToken != kCGDisplayFadeReservationInvalidToken)
+			{
+				CGDisplayFade(fadeToken, 0.3f, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, FALSE);
+				CGReleaseDisplayFadeReservation(fadeToken);
+			}
+		}
+
+		void MacOSRenderWindow::move(INT32 left, INT32 top)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			RenderWindowProperties& props = mProperties;
+			if (!props.isFullScreen)
+			{
+				mWindow->move(left, top);
+
+				Rect2I area = mWindow->getArea();
+				props.top = area.y;
+				props.left = area.x;
+
+				{
+					ScopedSpinLock lock(mLock);
+					mSyncedProperties.top = props.top;
+					mSyncedProperties.left = props.left;
+				}
+
+				mScreenArea = mWindow->_getScreenArea();
+				mNativeWindowArea = mWindow->_getScreenArea();
+
+				bs::RenderWindowManager::instance().notifySyncDataDirty(this);
+			}
+		}
+
+		void MacOSRenderWindow::resize(UINT32 width, UINT32 height)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			RenderWindowProperties& props = mProperties;
+			if (!props.isFullScreen)
+			{
+				mWindow->resize(width, height);
+
+				Rect2I area = mWindow->getArea();
+				props.width = area.width;
+				props.height = area.height;
+
+				{
+					ScopedSpinLock lock(mLock);
+					mSyncedProperties.width = props.width;
+					mSyncedProperties.height = props.height;
+				}
+
+				mScreenArea = mWindow->_getScreenArea();
+				mNativeWindowArea = mWindow->_getScreenArea();
+
+				bs::RenderWindowManager::instance().notifySyncDataDirty(this);
+			}
+		}
+
+		void MacOSRenderWindow::minimize()
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			mWindow->minimize();
+		}
+
+		void MacOSRenderWindow::maximize()
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			mWindow->maximize();
+		}
+
+		void MacOSRenderWindow::restore()
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			mWindow->restore();
+		}
+
+		void MacOSRenderWindow::setVSync(bool enabled, UINT32 interval)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			if(!enabled)
+				interval = 0;
+
+			mContext->setVSync(interval);
+
+			mProperties.vsync = enabled;
+			mProperties.vsyncInterval = interval;
+
+			{
+				ScopedSpinLock lock(mLock);
+				mSyncedProperties.vsync = enabled;
+				mSyncedProperties.vsyncInterval = interval;
+			}
+
+			bs::RenderWindowManager::instance().notifySyncDataDirty(this);
+		}
+
+		void MacOSRenderWindow::swapBuffers(UINT32 syncMask)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			if (mShowOnSwap)
+				setHidden(false);
+
+			mContext->swapBuffers();
+		}
+
+		void MacOSRenderWindow::copyToMemory(PixelData &dst, FrameBuffer buffer)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			if ((dst.getRight() > getProperties().width) ||
+				(dst.getBottom() > getProperties().height) ||
+				(dst.getFront() != 0) || (dst.getBack() != 1))
+			{
+				BS_EXCEPT(InvalidParametersException, "Invalid box.");
+			}
+
+			if (buffer == FB_AUTO)
+			{
+				buffer = mProperties.isFullScreen ? FB_FRONT : FB_BACK;
+			}
+
+			GLenum format = GLPixelUtil::getGLOriginFormat(dst.getFormat());
+			GLenum type = GLPixelUtil::getGLOriginDataType(dst.getFormat());
+
+			if ((format == GL_NONE) || (type == 0))
+			{
+				BS_EXCEPT(InvalidParametersException, "Unsupported format.");
+			}
+
+			// Must change the packing to ensure no overruns!
+			glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+			glReadBuffer((buffer == FB_FRONT)? GL_FRONT : GL_BACK);
+			glReadPixels((GLint)dst.getLeft(), (GLint)dst.getTop(),
+						 (GLsizei)dst.getWidth(), (GLsizei)dst.getHeight(),
+						 format, type, dst.getData());
+
+			// restore default alignment
+			glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+			//vertical flip
+			{
+				size_t rowSpan = dst.getWidth() * PixelUtil::getNumElemBytes(dst.getFormat());
+				size_t height = dst.getHeight();
+				UINT8* tmpData = (UINT8*)bs_alloc((UINT32)(rowSpan * height));
+				UINT8* srcRow = (UINT8 *)dst.getData(), *tmpRow = tmpData + (height - 1) * rowSpan;
+
+				while (tmpRow >= tmpData)
+				{
+					memcpy(tmpRow, srcRow, rowSpan);
+					srcRow += rowSpan;
+					tmpRow -= rowSpan;
+				}
+				memcpy(dst.getData(), tmpData, rowSpan * height);
+
+				bs_free(tmpData);
+			}
+		}
+
+		void MacOSRenderWindow::getCustomAttribute(const String& name, void* data) const
+		{
+			if(name == "GLCONTEXT")
+			{
+				SPtr<GLContext>* contextPtr = static_cast<SPtr<GLContext>*>(data);
+				*contextPtr = mContext;
+				return;
+			}
+			else if(name == "COCOA_WINDOW")
+			{
+				CocoaWindow** window = (CocoaWindow**)data;
+				*window = mWindow;
+				return;
+			}
+			else if(name == "WINDOW")
+			{
+				NSWindow** window = (NSWindow**)data;
+				*window = mWindow->_getPrivateData()->window;
+				return;
+			}
+		}
+
+		void MacOSRenderWindow::setActive(bool state)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			if(state)
+				mWindow->restore();
+			else
+				mWindow->minimize();
+
+			RenderWindow::setActive(state);
+		}
+
+		void MacOSRenderWindow::setHidden(bool hidden)
+		{
+			THROW_IF_NOT_CORE_THREAD;
+
+			if(!hidden)
+				mShowOnSwap = false;
+
+			if(hidden)
+				mWindow->hide();
+			else
+				mWindow->show();
+
+			RenderWindow::setHidden(hidden);
+		}
+
+		void MacOSRenderWindow::_windowMovedOrResized()
+		{
+			if (!mWindow)
+				return;
+
+			Lock lock(mDimensionsMutex);
+
+			RenderWindowProperties& props = mProperties;
+			if (!props.isFullScreen) // Fullscreen is handled directly by this object
+			{
+				Rect2I area = mWindow->getArea();
+
+				props.top = area.y;
+				props.left = area.x;
+				props.width = area.width;
+				props.height = area.height;
+			}
+
+			mScreenArea = mWindow->_getScreenArea();
+			mNativeWindowArea = mWindow->getArea(false);
+
+			RenderWindow::_windowMovedOrResized();
+		}
+
+		void MacOSRenderWindow::syncProperties()
+		{
+			ScopedSpinLock lock(mLock);
+			mProperties = mSyncedProperties;
+		}
+	}
+}
+

+ 102 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSVideoModeInfo.cpp

@@ -0,0 +1,102 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2017 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "MacOS/BsMacOSVideoModeInfo.h"
+#include <IOKit/graphics/IOGraphicsLib.h>
+
+namespace bs::ct
+{
+	MacOSVideoModeInfo::MacOSVideoModeInfo()
+	{
+		VideoModeInfo info;
+
+		CGDisplayCount numDisplays;
+		CGGetOnlineDisplayList(0, nullptr, &numDisplays);
+
+		auto displays = (CGDirectDisplayID*)bs_stack_alloc(sizeof(CGDirectDisplayID) * numDisplays);
+		for(UINT32 i = 0; i < numDisplays; i++)
+		{
+			if(CGDisplayMirrorsDisplay(displays[i]) != kCGNullDirectDisplay)
+				continue;
+
+			CGDisplayModeRef modeRef = CGDisplayCopyDisplayMode(displays[i]);
+			if(!modeRef)
+				continue;
+
+			VideoOutputInfo* output = bs_new<MacOSVideoOutputInfo>(displays[i], i);
+
+			// Make sure the primary output is the first in the output list
+			if(CGDisplayIsMain(displays[i]))
+				mOutputs.insert(mOutputs.begin(), output);
+			else
+				mOutputs.push_back(output);
+		}
+		bs_stack_free(displays);
+	}
+
+	MacOSVideoOutputInfo::MacOSVideoOutputInfo(CGDirectDisplayID displayID, UINT32 outputIdx)
+		:mDisplayID(displayID)
+	{
+		CGDisplayModeRef desktopModeRef = CGDisplayCopyDisplayMode(displayID);
+
+		CVDisplayLinkRef linkRef = nullptr;
+		CVDisplayLinkCreateWithCGDisplay(displayID, &linkRef);
+
+		io_service_t service = CGDisplayIOServicePort(displayID);
+		CFDictionaryRef deviceInfo = IODisplayCreateInfoDictionary(service, kIODisplayOnlyPreferredName);
+		auto locNames = (CFDictionaryRef)CFDictionaryGetValue(deviceInfo, kDisplayProductName);
+
+		CFIndex numNames = CFDictionaryGetCount(locNames);
+		if(numNames > 0)
+		{
+			auto keys = (CFStringRef*)bs_stack_alloc(numNames * sizeof(CFTypeRef));
+			CFDictionaryGetKeysAndValues(locNames, (const void**)keys, nullptr);
+
+			mName = CFStringGetCStringPtr(keys[0], kCFStringEncodingUTF8);
+			bs_stack_free(keys);
+		}
+
+		mDesktopVideoMode = new (bs_alloc<MacOSVideoMode>()) MacOSVideoMode(desktopModeRef, linkRef, outputIdx);
+
+		CFArrayRef modes = CGDisplayCopyAllDisplayModes(displayID, nullptr);
+		if(modes)
+		{
+			CFIndex count = CFArrayGetCount(modes);
+			for(int i = 0; i < count; i++)
+			{
+				auto modeRef = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
+				VideoMode* videoMode = new (bs_alloc<MacOSVideoMode>()) MacOSVideoMode(modeRef, linkRef, outputIdx);
+
+				mVideoModes.push_back(videoMode);
+			}
+
+			CFRelease(modes);
+		}
+	}
+
+	MacOSVideoMode::MacOSVideoMode(UINT32 width, UINT32 height, float refreshRate, UINT32 outputIdx)
+			:VideoMode(width, height, refreshRate, outputIdx), mModeRef(nullptr)
+	{ }
+
+	MacOSVideoMode::MacOSVideoMode(CGDisplayModeRef mode, CVDisplayLinkRef linkRef, UINT32 outputIdx)
+			:VideoMode(0, 0, 0.0f, outputIdx), mModeRef(mModeRef)
+	{
+		mWidth = (UINT32)CGDisplayModeGetPixelWidth(mModeRef);
+		mHeight = (UINT32)CGDisplayModeGetPixelHeight(mModeRef);
+
+		mRefreshRate = (float)CGDisplayModeGetRefreshRate(mModeRef);
+		if(mRefreshRate == 0.0f && linkRef != nullptr)
+		{
+			CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(linkRef);
+			if((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0)
+				mRefreshRate = time.timeScale / (float)time.timeValue;
+		}
+		mIsCustom = false;
+	}
+
+	MacOSVideoMode::~MacOSVideoMode()
+	{
+		if(!mIsCustom && mModeRef)
+			CGDisplayModeRelease(mModeRef);
+	}
+}
+

+ 54 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSVideoModeInfo.h

@@ -0,0 +1,54 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsGLPrerequisites.h"
+#include "RenderAPI/BsVideoModeInfo.h"
+#include <CoreGraphics/CoreGraphics.h>
+#include <CoreVideo/CoreVideo.h>
+
+namespace bs { namespace ct
+	{
+		/** @addtogroup GL
+		 *  @{
+		 */
+
+		/** @copydoc VideoMode */
+		class MacOSVideoMode : public VideoMode
+		{
+		public:
+			MacOSVideoMode(UINT32 width, UINT32 height, float refreshRate, UINT32 outputIdx);
+			~MacOSVideoMode() override;
+
+			/** Returns internal Core Graphics video mode reference. */
+			CGDisplayModeRef _getModeRef() const { return mModeRef; }
+
+		private:
+			MacOSVideoMode(CGDisplayModeRef modeRef, CVDisplayLinkRef linkRef, UINT32 outputIdx);
+			friend class MacOSVideoOutputInfo;
+
+			CGDisplayModeRef mModeRef;
+		};
+
+		/** @copydoc VideoOutputInfo */
+		class MacOSVideoOutputInfo : public VideoOutputInfo
+		{
+		public:
+			MacOSVideoOutputInfo(CGDirectDisplayID displayID, UINT32 outputIdx);
+
+			/** Returns the Core Graphics identifier for this display. */
+			CGDirectDisplayID _getDisplayID() const { return mDisplayID; }
+		private:
+			CGDirectDisplayID mDisplayID;
+		};
+
+		/** @copydoc VideoModeInfo */
+		class MacOSVideoModeInfo : public VideoModeInfo
+		{
+		public:
+			MacOSVideoModeInfo();
+		};
+
+		/** @} */
+	}}
+

+ 4 - 3
Source/CMake/GenerateScriptBindings.cmake

@@ -78,12 +78,13 @@ if(BansheeSBGen_FOUND)
 			-std=c++14
 			-DBS_STATIC_LIB
 			-DBS_SBGEN
-			-w
-			)
+			-w)
 
 		if(APPLE)
-			list(APPEND BS_GSB_COMMAND 
+			list(APPEND BS_GSB_COMMAND
 				-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1)
+			list(APPEND BS_GSB_COMMAND
+				-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include)
 			list(APPEND BS_GSB_COMMAND -stdlib=libc++)
 		endif()