BsWin32RenderWindow.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #ifndef _WIN32_WINNT
  4. #define _WIN32_WINNT 0x0500
  5. #endif
  6. #include "Win32/BsWin32RenderWindow.h"
  7. #include "Input/BsInput.h"
  8. #include "Renderapi/BsRenderAPI.h"
  9. #include "Corethread/BsCoreThread.h"
  10. #include "Error/BsException.h"
  11. #include "Win32/BsWin32GLSupport.h"
  12. #include "Win32/BsWin32Context.h"
  13. #include "Win32/BsWin32Platform.h"
  14. #include "Win32/BsWin32VideoModeInfo.h"
  15. #include "BsGLPixelFormat.h"
  16. #include "Managers/BsRenderWindowManager.h"
  17. #include "Win32/BsWin32Window.h"
  18. #include "Math/BsMath.h"
  19. GLenum GLEWAPIENTRY wglewContextInit(bs::ct::GLSupport* glSupport);
  20. namespace bs
  21. {
  22. #define _MAX_CLASS_NAME_ 128
  23. Win32RenderWindowProperties::Win32RenderWindowProperties(const RENDER_WINDOW_DESC& desc)
  24. :RenderWindowProperties(desc)
  25. { }
  26. Win32RenderWindow::Win32RenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, ct::Win32GLSupport &glsupport)
  27. :RenderWindow(desc, windowId), mGLSupport(glsupport), mProperties(desc)
  28. {
  29. }
  30. void Win32RenderWindow::getCustomAttribute(const String& name, void* pData) const
  31. {
  32. if (name == "WINDOW")
  33. {
  34. UINT64 *pHwnd = (UINT64*)pData;
  35. *pHwnd = (UINT64)getHWnd();
  36. return;
  37. }
  38. }
  39. Vector2I Win32RenderWindow::screenToWindowPos(const Vector2I& screenPos) const
  40. {
  41. POINT pos;
  42. pos.x = screenPos.x;
  43. pos.y = screenPos.y;
  44. ScreenToClient(getHWnd(), &pos);
  45. return Vector2I(pos.x, pos.y);
  46. }
  47. Vector2I Win32RenderWindow::windowToScreenPos(const Vector2I& windowPos) const
  48. {
  49. POINT pos;
  50. pos.x = windowPos.x;
  51. pos.y = windowPos.y;
  52. ClientToScreen(getHWnd(), &pos);
  53. return Vector2I(pos.x, pos.y);
  54. }
  55. SPtr<ct::Win32RenderWindow> Win32RenderWindow::getCore() const
  56. {
  57. return std::static_pointer_cast<ct::Win32RenderWindow>(mCoreSpecific);
  58. }
  59. void Win32RenderWindow::syncProperties()
  60. {
  61. ScopedSpinLock lock(getCore()->mLock);
  62. mProperties = getCore()->mSyncedProperties;
  63. }
  64. HWND Win32RenderWindow::getHWnd() const
  65. {
  66. blockUntilCoreInitialized();
  67. return getCore()->_getHWnd();
  68. }
  69. namespace ct
  70. {
  71. Win32RenderWindow::Win32RenderWindow(const RENDER_WINDOW_DESC& desc, UINT32 windowId, Win32GLSupport& glsupport)
  72. : RenderWindow(desc, windowId), mWindow(nullptr), mGLSupport(glsupport), mHDC(nullptr), mIsChild(false)
  73. , mDeviceName(nullptr), mDisplayFrequency(0), mShowOnSwap(false), mContext(nullptr), mProperties(desc)
  74. , mSyncedProperties(desc)
  75. { }
  76. Win32RenderWindow::~Win32RenderWindow()
  77. {
  78. Win32RenderWindowProperties& props = mProperties;
  79. if (mWindow != nullptr)
  80. {
  81. ReleaseDC(mWindow->getHWnd(), mHDC);
  82. bs_delete(mWindow);
  83. mWindow = nullptr;
  84. }
  85. props.mActive = false;
  86. mHDC = nullptr;
  87. if (mDeviceName != nullptr)
  88. {
  89. bs_free(mDeviceName);
  90. mDeviceName = nullptr;
  91. }
  92. }
  93. void Win32RenderWindow::initialize()
  94. {
  95. Win32RenderWindowProperties& props = mProperties;
  96. props.mIsFullScreen = mDesc.fullscreen;
  97. mIsChild = false;
  98. mDisplayFrequency = Math::roundToInt(mDesc.videoMode.getRefreshRate());
  99. props.mColorDepth = 32;
  100. WINDOW_DESC windowDesc;
  101. windowDesc.border = mDesc.border;
  102. windowDesc.enableDoubleClick = mDesc.enableDoubleClick;
  103. windowDesc.fullscreen = mDesc.fullscreen;
  104. windowDesc.width = mDesc.videoMode.getWidth();
  105. windowDesc.height = mDesc.videoMode.getHeight();
  106. windowDesc.hidden = mDesc.hidden || mDesc.hideUntilSwap;
  107. windowDesc.left = mDesc.left;
  108. windowDesc.top = mDesc.top;
  109. windowDesc.outerDimensions = mDesc.outerDimensions;
  110. windowDesc.title = mDesc.title;
  111. windowDesc.toolWindow = mDesc.toolWindow;
  112. windowDesc.creationParams = this;
  113. windowDesc.modal = mDesc.modal;
  114. windowDesc.wndProc = &Win32Platform::_win32WndProc;
  115. #ifdef BS_STATIC_LIB
  116. windowDesc.module = GetModuleHandle(NULL);
  117. #else
  118. windowDesc.module = GetModuleHandle(MODULE_NAME);
  119. #endif
  120. NameValuePairList::const_iterator opt;
  121. opt = mDesc.platformSpecific.find("parentWindowHandle");
  122. if (opt != mDesc.platformSpecific.end())
  123. windowDesc.parent = (HWND)parseUINT64(opt->second);
  124. opt = mDesc.platformSpecific.find("externalWindowHandle");
  125. if (opt != mDesc.platformSpecific.end())
  126. windowDesc.external = (HWND)parseUINT64(opt->second);
  127. const Win32VideoModeInfo& videoModeInfo = static_cast<const Win32VideoModeInfo&>(RenderAPI::instance().getVideoModeInfo());
  128. UINT32 numOutputs = videoModeInfo.getNumOutputs();
  129. if (numOutputs > 0)
  130. {
  131. UINT32 actualMonitorIdx = std::min(mDesc.videoMode.getOutputIdx(), numOutputs - 1);
  132. const Win32VideoOutputInfo& outputInfo = static_cast<const Win32VideoOutputInfo&>(videoModeInfo.getOutputInfo(actualMonitorIdx));
  133. windowDesc.monitor = outputInfo.getMonitorHandle();
  134. }
  135. mIsChild = windowDesc.parent != nullptr;
  136. props.mIsFullScreen = mDesc.fullscreen && !mIsChild;
  137. props.mColorDepth = 32;
  138. props.mActive = true;
  139. if (!windowDesc.external)
  140. {
  141. mShowOnSwap = mDesc.hideUntilSwap;
  142. props.mHidden = mDesc.hideUntilSwap || mDesc.hidden;
  143. }
  144. mWindow = bs_new<Win32Window>(windowDesc);
  145. props.mWidth = mWindow->getWidth();
  146. props.mHeight = mWindow->getHeight();
  147. props.mTop = mWindow->getTop();
  148. props.mLeft = mWindow->getLeft();
  149. if (!windowDesc.external)
  150. {
  151. if (props.mIsFullScreen)
  152. {
  153. DEVMODE displayDeviceMode;
  154. memset(&displayDeviceMode, 0, sizeof(displayDeviceMode));
  155. displayDeviceMode.dmSize = sizeof(DEVMODE);
  156. displayDeviceMode.dmBitsPerPel = props.mColorDepth;
  157. displayDeviceMode.dmPelsWidth = props.mWidth;
  158. displayDeviceMode.dmPelsHeight = props.mHeight;
  159. displayDeviceMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
  160. if (mDisplayFrequency)
  161. {
  162. displayDeviceMode.dmDisplayFrequency = mDisplayFrequency;
  163. displayDeviceMode.dmFields |= DM_DISPLAYFREQUENCY;
  164. if (ChangeDisplaySettingsEx(mDeviceName, &displayDeviceMode, NULL, CDS_FULLSCREEN | CDS_TEST, NULL) != DISP_CHANGE_SUCCESSFUL)
  165. {
  166. BS_EXCEPT(RenderingAPIException, "ChangeDisplaySettings with user display frequency failed.");
  167. }
  168. }
  169. if (ChangeDisplaySettingsEx(mDeviceName, &displayDeviceMode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)
  170. {
  171. BS_EXCEPT(RenderingAPIException, "ChangeDisplaySettings failed.");
  172. }
  173. }
  174. }
  175. mHDC = GetDC(mWindow->getHWnd());
  176. int testMultisample = props.mMultisampleCount;
  177. bool testHwGamma = mDesc.gamma;
  178. bool formatOk = mGLSupport.selectPixelFormat(mHDC, props.mColorDepth, testMultisample, testHwGamma, mDesc.depthBuffer);
  179. if (!formatOk)
  180. {
  181. if (props.mMultisampleCount > 0)
  182. {
  183. // Try without multisampling
  184. testMultisample = 0;
  185. formatOk = mGLSupport.selectPixelFormat(mHDC, props.mColorDepth, testMultisample, testHwGamma, mDesc.depthBuffer);
  186. }
  187. if (!formatOk && mDesc.gamma)
  188. {
  189. // Try without sRGB
  190. testHwGamma = false;
  191. testMultisample = props.mMultisampleCount;
  192. formatOk = mGLSupport.selectPixelFormat(mHDC, props.mColorDepth, testMultisample, testHwGamma, mDesc.depthBuffer);
  193. }
  194. if (!formatOk && mDesc.gamma && (props.mMultisampleCount > 0))
  195. {
  196. // Try without both
  197. testHwGamma = false;
  198. testMultisample = 0;
  199. formatOk = mGLSupport.selectPixelFormat(mHDC, props.mColorDepth, testMultisample, testHwGamma, mDesc.depthBuffer);
  200. }
  201. if (!formatOk)
  202. BS_EXCEPT(RenderingAPIException, "Failed selecting pixel format.");
  203. }
  204. // Record what gamma option we used in the end
  205. // this will control enabling of sRGB state flags when used
  206. props.mHwGamma = testHwGamma;
  207. props.mMultisampleCount = testMultisample;
  208. mContext = mGLSupport.createContext(mHDC, nullptr);
  209. {
  210. ScopedSpinLock lock(mLock);
  211. mSyncedProperties = props;
  212. }
  213. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  214. RenderWindow::initialize();
  215. }
  216. void Win32RenderWindow::setFullscreen(UINT32 width, UINT32 height, float refreshRate, UINT32 monitorIdx)
  217. {
  218. THROW_IF_NOT_CORE_THREAD;
  219. if (mIsChild)
  220. return;
  221. const Win32VideoModeInfo& videoModeInfo = static_cast<const Win32VideoModeInfo&>(RenderAPI::instance().getVideoModeInfo());
  222. UINT32 numOutputs = videoModeInfo.getNumOutputs();
  223. if (numOutputs == 0)
  224. return;
  225. Win32RenderWindowProperties& props = mProperties;
  226. UINT32 actualMonitorIdx = std::min(monitorIdx, numOutputs - 1);
  227. const Win32VideoOutputInfo& outputInfo = static_cast<const Win32VideoOutputInfo&>(videoModeInfo.getOutputInfo(actualMonitorIdx));
  228. mDisplayFrequency = Math::roundToInt(refreshRate);
  229. props.mIsFullScreen = true;
  230. DEVMODE displayDeviceMode;
  231. memset(&displayDeviceMode, 0, sizeof(displayDeviceMode));
  232. displayDeviceMode.dmSize = sizeof(DEVMODE);
  233. displayDeviceMode.dmBitsPerPel = props.mColorDepth;
  234. displayDeviceMode.dmPelsWidth = width;
  235. displayDeviceMode.dmPelsHeight = height;
  236. displayDeviceMode.dmDisplayFrequency = mDisplayFrequency;
  237. displayDeviceMode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
  238. HMONITOR hMonitor = outputInfo.getMonitorHandle();
  239. MONITORINFOEX monitorInfo;
  240. memset(&monitorInfo, 0, sizeof(MONITORINFOEX));
  241. monitorInfo.cbSize = sizeof(MONITORINFOEX);
  242. GetMonitorInfo(hMonitor, &monitorInfo);
  243. if (ChangeDisplaySettingsEx(monitorInfo.szDevice, &displayDeviceMode, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)
  244. {
  245. BS_EXCEPT(RenderingAPIException, "ChangeDisplaySettings failed");
  246. }
  247. props.mTop = monitorInfo.rcMonitor.top;
  248. props.mLeft = monitorInfo.rcMonitor.left;
  249. props.mWidth = width;
  250. props.mHeight = height;
  251. SetWindowLong(mWindow->getHWnd(), GWL_STYLE, WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
  252. SetWindowLong(mWindow->getHWnd(), GWL_EXSTYLE, 0);
  253. SetWindowPos(mWindow->getHWnd(), HWND_TOP, props.mLeft, props.mTop, width, height, SWP_NOOWNERZORDER | SWP_FRAMECHANGED);
  254. _windowMovedOrResized();
  255. }
  256. void Win32RenderWindow::setFullscreen(const VideoMode& mode)
  257. {
  258. THROW_IF_NOT_CORE_THREAD;
  259. setFullscreen(mode.getWidth(), mode.getHeight(), mode.getRefreshRate(), mode.getOutputIdx());
  260. }
  261. void Win32RenderWindow::setWindowed(UINT32 width, UINT32 height)
  262. {
  263. THROW_IF_NOT_CORE_THREAD;
  264. Win32RenderWindowProperties& props = mProperties;
  265. if (!props.mIsFullScreen)
  266. return;
  267. props.mIsFullScreen = false;
  268. props.mWidth = width;
  269. props.mHeight = height;
  270. // Drop out of fullscreen
  271. ChangeDisplaySettingsEx(mDeviceName, NULL, NULL, 0, NULL);
  272. UINT32 winWidth = width;
  273. UINT32 winHeight = height;
  274. RECT rect;
  275. SetRect(&rect, 0, 0, winWidth, winHeight);
  276. AdjustWindowRect(&rect, mWindow->getStyle(), false);
  277. winWidth = rect.right - rect.left;
  278. winHeight = rect.bottom - rect.top;
  279. // Deal with centering when switching down to smaller resolution
  280. HMONITOR hMonitor = MonitorFromWindow(mWindow->getHWnd(), MONITOR_DEFAULTTONEAREST);
  281. MONITORINFO monitorInfo;
  282. memset(&monitorInfo, 0, sizeof(MONITORINFO));
  283. monitorInfo.cbSize = sizeof(MONITORINFO);
  284. GetMonitorInfo(hMonitor, &monitorInfo);
  285. LONG screenw = monitorInfo.rcWork.right - monitorInfo.rcWork.left;
  286. LONG screenh = monitorInfo.rcWork.bottom - monitorInfo.rcWork.top;
  287. INT32 left = screenw > INT32(winWidth) ? ((screenw - INT32(winWidth)) / 2) : 0;
  288. INT32 top = screenh > INT32(winHeight) ? ((screenh - INT32(winHeight)) / 2) : 0;
  289. SetWindowLong(mWindow->getHWnd(), GWL_STYLE, mWindow->getStyle() | WS_VISIBLE);
  290. SetWindowLong(mWindow->getHWnd(), GWL_EXSTYLE, mWindow->getStyleEx());
  291. SetWindowPos(mWindow->getHWnd(), HWND_NOTOPMOST, left, top, winWidth, winHeight,
  292. SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOACTIVATE);
  293. {
  294. ScopedSpinLock lock(mLock);
  295. mSyncedProperties.mWidth = props.mWidth;
  296. mSyncedProperties.mHeight = props.mHeight;
  297. }
  298. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  299. _windowMovedOrResized();
  300. }
  301. void Win32RenderWindow::move(INT32 left, INT32 top)
  302. {
  303. THROW_IF_NOT_CORE_THREAD;
  304. Win32RenderWindowProperties& props = mProperties;
  305. if (!props.mIsFullScreen)
  306. {
  307. mWindow->move(left, top);
  308. props.mTop = mWindow->getTop();
  309. props.mLeft = mWindow->getLeft();
  310. {
  311. ScopedSpinLock lock(mLock);
  312. mSyncedProperties.mTop = props.mTop;
  313. mSyncedProperties.mLeft = props.mLeft;
  314. }
  315. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  316. }
  317. }
  318. void Win32RenderWindow::resize(UINT32 width, UINT32 height)
  319. {
  320. THROW_IF_NOT_CORE_THREAD;
  321. Win32RenderWindowProperties& props = mProperties;
  322. if (!props.mIsFullScreen)
  323. {
  324. mWindow->resize(width, height);
  325. props.mWidth = mWindow->getWidth();
  326. props.mHeight = mWindow->getHeight();
  327. {
  328. ScopedSpinLock lock(mLock);
  329. mSyncedProperties.mWidth = props.mWidth;
  330. mSyncedProperties.mHeight = props.mHeight;
  331. }
  332. bs::RenderWindowManager::instance().notifySyncDataDirty(this);
  333. }
  334. }
  335. void Win32RenderWindow::minimize()
  336. {
  337. THROW_IF_NOT_CORE_THREAD;
  338. mWindow->minimize();
  339. }
  340. void Win32RenderWindow::maximize()
  341. {
  342. THROW_IF_NOT_CORE_THREAD;
  343. mWindow->maximize();
  344. }
  345. void Win32RenderWindow::restore()
  346. {
  347. THROW_IF_NOT_CORE_THREAD;
  348. mWindow->restore();
  349. }
  350. void Win32RenderWindow::swapBuffers(UINT32 syncMask)
  351. {
  352. THROW_IF_NOT_CORE_THREAD;
  353. if (mShowOnSwap)
  354. setHidden(false);
  355. SwapBuffers(mHDC);
  356. }
  357. void Win32RenderWindow::copyToMemory(PixelData &dst, FrameBuffer buffer)
  358. {
  359. THROW_IF_NOT_CORE_THREAD;
  360. if ((dst.getRight() > getProperties().getWidth()) ||
  361. (dst.getBottom() > getProperties().getHeight()) ||
  362. (dst.getFront() != 0) || (dst.getBack() != 1))
  363. {
  364. BS_EXCEPT(InvalidParametersException, "Invalid box.");
  365. }
  366. if (buffer == FB_AUTO)
  367. {
  368. buffer = mProperties.isFullScreen() ? FB_FRONT : FB_BACK;
  369. }
  370. GLenum format = GLPixelUtil::getGLOriginFormat(dst.getFormat());
  371. GLenum type = GLPixelUtil::getGLOriginDataType(dst.getFormat());
  372. if ((format == GL_NONE) || (type == 0))
  373. {
  374. BS_EXCEPT(InvalidParametersException, "Unsupported format.");
  375. }
  376. // Must change the packing to ensure no overruns!
  377. glPixelStorei(GL_PACK_ALIGNMENT, 1);
  378. glReadBuffer((buffer == FB_FRONT)? GL_FRONT : GL_BACK);
  379. glReadPixels((GLint)dst.getLeft(), (GLint)dst.getTop(),
  380. (GLsizei)dst.getWidth(), (GLsizei)dst.getHeight(),
  381. format, type, dst.getData());
  382. // restore default alignment
  383. glPixelStorei(GL_PACK_ALIGNMENT, 4);
  384. //vertical flip
  385. {
  386. size_t rowSpan = dst.getWidth() * PixelUtil::getNumElemBytes(dst.getFormat());
  387. size_t height = dst.getHeight();
  388. UINT8* tmpData = (UINT8*)bs_alloc((UINT32)(rowSpan * height));
  389. UINT8* srcRow = (UINT8 *)dst.getData(), *tmpRow = tmpData + (height - 1) * rowSpan;
  390. while (tmpRow >= tmpData)
  391. {
  392. memcpy(tmpRow, srcRow, rowSpan);
  393. srcRow += rowSpan;
  394. tmpRow -= rowSpan;
  395. }
  396. memcpy(dst.getData(), tmpData, rowSpan * height);
  397. bs_free(tmpData);
  398. }
  399. }
  400. void Win32RenderWindow::getCustomAttribute(const String& name, void* pData) const
  401. {
  402. if(name == "GLCONTEXT")
  403. {
  404. SPtr<GLContext>* contextPtr = static_cast<SPtr<GLContext>*>(pData);
  405. *contextPtr = mContext;
  406. return;
  407. }
  408. else if(name == "WINDOW")
  409. {
  410. UINT64 *pHwnd = (UINT64*)pData;
  411. *pHwnd = (UINT64)_getHWnd();
  412. return;
  413. }
  414. }
  415. void Win32RenderWindow::setActive(bool state)
  416. {
  417. THROW_IF_NOT_CORE_THREAD;
  418. mWindow->setActive(state);
  419. RenderWindow::setActive(state);
  420. }
  421. void Win32RenderWindow::setHidden(bool hidden)
  422. {
  423. THROW_IF_NOT_CORE_THREAD;
  424. mShowOnSwap = false;
  425. mWindow->setHidden(hidden);
  426. RenderWindow::setHidden(hidden);
  427. }
  428. void Win32RenderWindow::_windowMovedOrResized()
  429. {
  430. if (!mWindow)
  431. return;
  432. mWindow->_windowMovedOrResized();
  433. Win32RenderWindowProperties& props = mProperties;
  434. if (!props.mIsFullScreen) // Fullscreen is handled directly by this object
  435. {
  436. props.mTop = mWindow->getTop();
  437. props.mLeft = mWindow->getLeft();
  438. props.mWidth = mWindow->getWidth();
  439. props.mHeight = mWindow->getHeight();
  440. }
  441. RenderWindow::_windowMovedOrResized();
  442. }
  443. HWND Win32RenderWindow::_getHWnd() const
  444. {
  445. return mWindow->getHWnd();
  446. }
  447. void Win32RenderWindow::syncProperties()
  448. {
  449. ScopedSpinLock lock(mLock);
  450. mProperties = mSyncedProperties;
  451. }
  452. }
  453. }