Procházet zdrojové kódy

WIP: Linux port
- Transparent window rendering fixed
- Window with -1/-1 coordinates will now be centered on chosen monitor
- Scene object serialization was broken when performing record/restore operation, due to child/component ordering issue
- ScriptObjects now register themselves on load

Marko Pintera před 8 roky
rodič
revize
e7fd5cbe10

+ 13 - 4
Source/BansheeCore/Linux/BsLinuxPlatform.cpp

@@ -1261,13 +1261,22 @@ namespace bs
 			mData->mainXWindow = 0;
 	}
 
-	Pixmap LinuxPlatform::createPixmap(const PixelData& data)
+	Pixmap LinuxPlatform::createPixmap(const PixelData& data, UINT32 depth)
 	{
+		// Premultiply alpha
+		Vector<Color> colors = data.getColors();
+		for(auto& color : colors)
+		{
+			color.r *= color.a;
+			color.g *= color.a;
+			color.b *= color.a;
+		}
+
+		// Convert to BGRA
 		SPtr<PixelData> bgraData = PixelData::create(data.getWidth(), data.getHeight(), 1, PF_BGRA8);
-		PixelUtil::bulkPixelConversion(data, *bgraData);
+		bgraData->setColors(colors);
 
-		UINT32 depth = (UINT32)XDefaultDepth(mData->xDisplay, 0);
-		XImage* image = XCreateImage(mData->xDisplay, XDefaultVisual(mData->xDisplay, 0), depth, ZPixmap, 0,
+		XImage* image = XCreateImage(mData->xDisplay, CopyFromParent, depth, ZPixmap, 0,
 				(char*)bgraData->getData(), data.getWidth(), data.getHeight(), 32, 0);
 
 		Pixmap pixmap = XCreatePixmap(mData->xDisplay, XDefaultRootWindow(mData->xDisplay),

+ 1 - 1
Source/BansheeCore/Linux/BsLinuxPlatform.h

@@ -39,7 +39,7 @@ namespace bs
 		static void _unregisterWindow(::Window xWindow);
 
 		/** Generates a X11 Pixmap from the provided pixel data. */
-		static Pixmap createPixmap(const PixelData& data);
+		static Pixmap createPixmap(const PixelData& data, UINT32 depth);
 	};
 
 	/** @} */

+ 94 - 21
Source/BansheeCore/Linux/BsLinuxWindow.cpp

@@ -5,6 +5,7 @@
 #include "BsLinuxDropTarget.h"
 
 #include <X11/Xatom.h>
+#include <X11/extensions/Xrandr.h>
 
 #define _NET_WM_STATE_REMOVE 0
 #define _NET_WM_STATE_ADD 1
@@ -45,43 +46,112 @@ namespace bs
 
 		::Display* display = LinuxPlatform::getXDisplay();
 
-		INT32 screen;
-		if(desc.screen == (UINT32)-1)
-			screen = XDefaultScreen(display);
-		else
-			screen = std::min((INT32)desc.screen, XScreenCount(display));
+		// Find the screen of the chosen monitor, as well as its current dimensions
+		INT32 screen = XDefaultScreen(display);
+		UINT32 outputIdx = 0;
+
+		RROutput primaryOutput = XRRGetOutputPrimary(display, RootWindow(display, screen));
+		INT32 monitorX = 0;
+		INT32 monitorY = 0;
+		UINT32 monitorWidth = 0;
+		UINT32 monitorHeight = 0;
+
+		INT32 screenCount = XScreenCount(display);
+		for(INT32 i = 0; i < screenCount; i++)
+		{
+			XRRScreenResources* screenRes = XRRGetScreenResources(display, RootWindow(display, i));
+
+			bool foundMonitor = false;
+			for (INT32 j = 0; j < screenRes->noutput; j++)
+			{
+				XRROutputInfo* outputInfo = XRRGetOutputInfo(display, screenRes, screenRes->outputs[j]);
+				if (outputInfo == nullptr || outputInfo->crtc == 0 || outputInfo->connection == RR_Disconnected)
+				{
+					XRRFreeOutputInfo(outputInfo);
+
+					continue;
+				}
+
+				XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screenRes, outputInfo->crtc);
+				if (crtcInfo == nullptr)
+				{
+					XRRFreeCrtcInfo(crtcInfo);
+					XRRFreeOutputInfo(outputInfo);
+
+					continue;
+				}
+
+				if(desc.screen == (UINT32)-1)
+				{
+					if(screenRes->outputs[j] == primaryOutput)
+						foundMonitor = true;
+				}
+				else
+					foundMonitor = outputIdx == desc.screen;
+
+				if(foundMonitor)
+				{
+					screen = i;
+
+					monitorX = crtcInfo->x;
+					monitorY = crtcInfo->y;
+					monitorWidth = crtcInfo->width;
+					monitorHeight = crtcInfo->height;
+
+					foundMonitor = true;
+					break;
+				}
+			}
+
+			if(foundMonitor)
+				break;
+		}
 
 		XSetWindowAttributes attributes;
 		attributes.background_pixel = XWhitePixel(display, screen);
 		attributes.border_pixel = XBlackPixel(display, screen);
+		attributes.background_pixmap = 0;
 
 		attributes.colormap = XCreateColormap(display,
 				XRootWindow(display, screen),
 				desc.visualInfo.visual,
 				AllocNone);
 
-		UINT32 borderWidth = 0;
+		// If no position specified, center on the requested monitor
+		if (desc.x == -1)
+			m->x = monitorX + (monitorWidth - desc.width) / 2;
+		else if (desc.screen != (UINT32)-1)
+			m->x = monitorX + desc.x;
+		else
+			m->x = desc.x;
+
+		if (desc.y == -1)
+			m->y = monitorY + (monitorHeight - desc.height) / 2;
+		else if (desc.screen != (UINT32)-1)
+			m->y = monitorY + desc.y;
+		else
+			m->y = desc.y;
 
-		m->x = desc.x;
-		m->y = desc.y;
 		m->width = desc.width;
 		m->height = desc.height;
+
 		m->xWindow = XCreateWindow(display,
 				XRootWindow(display, screen),
-				desc.x, desc.y,
-				desc.width, desc.height,
-				borderWidth, desc.visualInfo.depth,
+				m->x, m->y,
+				m->width, m->height,
+				0, desc.visualInfo.depth,
 				InputOutput, desc.visualInfo.visual,
-				CWBackPixel | CWBorderPixel | CWColormap, &attributes);
+				CWBackPixel | CWBorderPixel | CWColormap | CWBackPixmap, &attributes);
 
 		XStoreName(display, m->xWindow, desc.title.c_str());
 
+		// Position/size might have (and usually will) get overridden by the WM, so re-apply them
 		XSizeHints hints;
 		hints.flags = PPosition | PSize;
-		hints.x = desc.x;
-		hints.y = desc.y;
-		hints.width = desc.width;
-		hints.height = desc.height;
+		hints.x = m->x;
+		hints.y = m->y;
+		hints.width = m->width;
+		hints.height = m->height;
 
 		if(!desc.allowResize)
 		{
@@ -123,9 +193,11 @@ namespace bs
 		// Set background image if assigned
 		if(desc.background)
 		{
-			Pixmap pixmap = LinuxPlatform::createPixmap(*desc.background);
+			Pixmap pixmap = LinuxPlatform::createPixmap(*desc.background, (UINT32)desc.visualInfo.depth);
+
 			XSetWindowBackgroundPixmap(display, m->xWindow, pixmap);
 			XFreePixmap(display, pixmap);
+			XSync(display, 0);
 		}
 
 		if(!desc.showOnTaskBar)
@@ -273,17 +345,18 @@ namespace bs
 
 	void LinuxWindow::setIcon(const PixelData& data)
 	{
-		Pixmap iconPixmap = LinuxPlatform::createPixmap(data);
+		::Display* display = LinuxPlatform::getXDisplay();
+		Pixmap iconPixmap = LinuxPlatform::createPixmap(data, (UINT32)XDefaultDepth(display, XDefaultScreen(display)));
 
 		XWMHints* hints = XAllocWMHints();
 		hints->flags = IconPixmapHint;
 		hints->icon_pixmap = iconPixmap;
 
-		XSetWMHints(LinuxPlatform::getXDisplay(), m->xWindow, hints);
-		XFlush(LinuxPlatform::getXDisplay());
+		XSetWMHints(display, m->xWindow, hints);
+		XFlush(display);
 
 		XFree(hints);
-		XFreePixmap(LinuxPlatform::getXDisplay(), iconPixmap);
+		XFreePixmap(display, iconPixmap);
 	}
 
 	void LinuxWindow::_cleanUp()

+ 17 - 5
Source/BansheeCore/RTTI/BsSceneObjectRTTI.h

@@ -40,7 +40,6 @@ namespace bs
 		bool& getActive(SceneObject* obj) { return obj->mActiveSelf; }
 		void setActive(SceneObject* obj, bool& value) { obj->mActiveSelf = value; }
 
-		// NOTE - These can only be set sequentially, specific array index is ignored
 		SPtr<SceneObject> getChild(SceneObject* obj, UINT32 idx) { return obj->mChildren[idx].getInternalPtr(); }
 		void setChild(SceneObject* obj, UINT32 idx, SPtr<SceneObject> param)
 		{
@@ -48,8 +47,13 @@ namespace bs
 			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
 			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
 
-			soDeserializationData.children.push_back(param);
-		} 
+			// It's important that child indices remain the same after deserialization, as some systems (like SO
+			// record/restore) depend on it
+			if(idx >= soDeserializationData.children.size())
+				soDeserializationData.children.resize(idx + 1);
+
+			soDeserializationData.children[idx] = param;
+		}
 
 		UINT32 getNumChildren(SceneObject* obj) { return (UINT32)obj->mChildren.size(); }
 		void setNumChildren(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
@@ -62,7 +66,12 @@ namespace bs
 			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
 			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
 
-			soDeserializationData.components.push_back(param);
+			// It's important that child indices remain the same after deserialization, as some systems (like SO
+			// record/restore) depend on it
+			if(idx >= soDeserializationData.components.size())
+				soDeserializationData.components.resize(idx + 1);
+
+			soDeserializationData.components[idx] = param;
 		}
 		UINT32 getNumComponents(SceneObject* obj) { return (UINT32)obj->mComponents.size(); }
 		void setNumComponents(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
@@ -151,7 +160,10 @@ namespace bs
 				so->addComponentInternal(component);
 
 			for (auto& child : soDeserializationData.children)
-				child->_setParent(so->mThisHandle, false);
+			{
+				if(child != nullptr)
+					child->_setParent(so->mThisHandle, false);
+			}
 
 			// If this is the deserialization parent, end deserialization (which resolves all game object handles, if we 
 			// provided valid IDs), and instantiate (i.e. activate) the deserialized hierarchy.

+ 6 - 6
Source/BansheeEditor/Utility/BsEditorUtility.cpp

@@ -168,23 +168,23 @@ namespace bs
 
 		struct TempData
 		{
-			TempData(SceneObjProxy& proxy, const HSceneObject& restoredObj)
+			TempData(SceneObjProxy* proxy, const HSceneObject& restoredObj)
 				:proxy(proxy), restoredObj(restoredObj)
 			{ }
 
-			SceneObjProxy& proxy;
+			SceneObjProxy* proxy;
 			HSceneObject restoredObj;
 		};
 
 		Stack<TempData> todo;
-		todo.push(TempData(proxy, restored));
+		todo.push(TempData(&proxy, restored));
 
 		while (!todo.empty())
 		{
 			TempData data = todo.top();
 			todo.pop();
 
-			data.restoredObj->_setInstanceData(data.proxy.instanceData);
+			data.restoredObj->_setInstanceData(data.proxy->instanceData);
 
 			// Find components that are still active and swap the old ones with restored ones,
 			// keep any other as is.
@@ -193,7 +193,7 @@ namespace bs
 			UINT32 idx = 0;
 			for (auto& restoredComponent : restoredComponents)
 			{
-				restoredComponent->_setInstanceData(data.proxy.componentInstanceData[idx]);
+				restoredComponent->_setInstanceData(data.proxy->componentInstanceData[idx]);
 
 				SPtr<GameObject> restoredPtr = std::static_pointer_cast<GameObject>(restoredComponent.getInternalPtr());
 				HComponent restoredComponentCopy = restoredComponent; // To remove const
@@ -207,7 +207,7 @@ namespace bs
 			UINT32 restoredNumChildren = data.restoredObj->getNumChildren();
 			for (UINT32 i = 0; i < restoredNumChildren; i++)
 			{
-				todo.push(TempData(data.proxy.children[i], data.restoredObj->getChild(i)));
+				todo.push(TempData(&data.proxy->children[i], data.restoredObj->getChild(i)));
 			}
 		}
 	}

+ 7 - 1
Source/BansheeEngine/Platform/BsSplashScreen.cpp

@@ -4,6 +4,7 @@
 #include "Resources/BsBuiltinResources.h"
 #include "Utility/BsTimer.h"
 #include "CoreThread/BsCoreThread.h"
+#include <X11/Xutil.h>
 
 #if BS_PLATFORM == BS_PLATFORM_WIN32
 #include "Win32/BsWin32Platform.h"
@@ -124,10 +125,15 @@ namespace bs
 		windowDesc.background = splashPixelData;
 
 		LinuxPlatform::lockX();
-		windowDesc.screen = XDefaultScreen(LinuxPlatform::getXDisplay());
+		::Display* display = LinuxPlatform::getXDisplay();
+
+		windowDesc.screen = (UINT32)XDefaultScreen(display);
 		windowDesc.visualInfo.depth = XDefaultDepth(LinuxPlatform::getXDisplay(), windowDesc.screen);
 		windowDesc.visualInfo.visual = XDefaultVisual(LinuxPlatform::getXDisplay(), windowDesc.screen);
 
+		// Get a RGBA visual
+		XMatchVisualInfo(display, DefaultScreen(display), 32, TrueColor, &windowDesc.visualInfo);
+
 		m->window = bs_new<LinuxWindow>(windowDesc);
 		LinuxPlatform::unlockX();
 

+ 4 - 0
Source/BansheeGLRenderAPI/Linux/BsLinuxVideoModeInfo.cpp

@@ -38,7 +38,11 @@ namespace bs { namespace ct
 
 				XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screenRes, outputInfo->crtc);
 				if(crtcInfo == nullptr)
+				{
+					XRRFreeCrtcInfo(crtcInfo);
+					XRRFreeOutputInfo(outputInfo);
 					continue;
+				}
 
 				VideoOutputInfo* output = bs_new<LinuxVideoOutputInfo>(display, i, outputInfo, crtcInfo, screenRes,
 						screenRes->outputs[j], (UINT32)mOutputs.size());

+ 8 - 3
Source/SBansheeEngine/BsScriptObject.h

@@ -87,6 +87,8 @@ namespace bs
 		{
 			ScriptObject<Type, Base>::_initMetaData();
 		}
+
+		void makeSureIAmInstantiated() { }
 	};
 
 	/**	Template version of ScriptObjectBase populates the object meta-data on library load. */
@@ -96,7 +98,9 @@ namespace bs
 	public:
 		ScriptObject(MonoObject* instance)
 			:Base(instance)
-		{	
+		{
+			initOnStart.makeSureIAmInstantiated();
+
 			Type* param = (Type*)(Base*)this; // Needed due to multiple inheritance. Safe since Type must point to an class derived from this one.
 
 			if(metaData.thisPtrField != nullptr)
@@ -158,6 +162,7 @@ namespace bs
 		 */
 		static void _initMetaData()
 		{
+			assert(metaData.name.empty());
 			metaData = ScriptMeta(Type::getAssemblyName(), Type::getNamespace(), Type::getTypeName(), &Type::initRuntimeData);
 
 			MonoManager::registerScriptType(&metaData);
@@ -167,11 +172,11 @@ namespace bs
 		static ScriptMeta metaData;
 
 	private:
-		static volatile InitScriptObjectOnStart<Type, Base> initOnStart;
+		static InitScriptObjectOnStart<Type, Base> initOnStart;
 	};
 
 	template <typename Type, typename Base>
-	volatile InitScriptObjectOnStart<Type, Base> ScriptObject<Type, Base>::initOnStart;
+	InitScriptObjectOnStart<Type, Base> ScriptObject<Type, Base>::initOnStart;
 
 	template <typename Type, typename Base>
 	ScriptMeta ScriptObject<Type, Base>::metaData;