Graphics.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. //
  2. // Copyright (c) 2008-2020 the Urho3D project.
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include "../Precompiled.h"
  23. #include "../Core/Profiler.h"
  24. #include "../Graphics/AnimatedModel.h"
  25. #include "../Graphics/Animation.h"
  26. #include "../Graphics/AnimationController.h"
  27. #include "../Graphics/Camera.h"
  28. #include "../Graphics/CustomGeometry.h"
  29. #include "../Graphics/DebugRenderer.h"
  30. #include "../Graphics/DecalSet.h"
  31. #include "../Graphics/Graphics.h"
  32. #include "../Graphics/GraphicsEvents.h"
  33. #include "../Graphics/GraphicsImpl.h"
  34. #include "../Graphics/Material.h"
  35. #include "../Graphics/Octree.h"
  36. #include "../Graphics/ParticleEffect.h"
  37. #include "../Graphics/ParticleEmitter.h"
  38. #include "../Graphics/RibbonTrail.h"
  39. #include "../Graphics/Shader.h"
  40. #include "../Graphics/ShaderPrecache.h"
  41. #include "../Graphics/Skybox.h"
  42. #include "../Graphics/StaticModelGroup.h"
  43. #include "../Graphics/Technique.h"
  44. #include "../Graphics/Terrain.h"
  45. #include "../Graphics/TerrainPatch.h"
  46. #ifdef _WIN32
  47. #include "../Graphics/Texture2D.h"
  48. #endif
  49. #include "../Graphics/Texture2DArray.h"
  50. #include "../Graphics/Texture3D.h"
  51. #include "../Graphics/TextureCube.h"
  52. #include "../Graphics/Zone.h"
  53. #include "../IO/FileSystem.h"
  54. #include "../IO/Log.h"
  55. #include <SDL/SDL.h>
  56. #include "../DebugNew.h"
  57. namespace Urho3D
  58. {
  59. void Graphics::SetExternalWindow(void* window)
  60. {
  61. if (!window_)
  62. externalWindow_ = window;
  63. else
  64. URHO3D_LOGERROR("Window already opened, can not set external window");
  65. }
  66. void Graphics::SetWindowTitle(const String& windowTitle)
  67. {
  68. windowTitle_ = windowTitle;
  69. if (window_)
  70. SDL_SetWindowTitle(window_, windowTitle_.CString());
  71. }
  72. void Graphics::SetWindowIcon(Image* windowIcon)
  73. {
  74. windowIcon_ = windowIcon;
  75. if (window_)
  76. CreateWindowIcon();
  77. }
  78. void Graphics::SetWindowPosition(const IntVector2& position)
  79. {
  80. if (window_)
  81. SDL_SetWindowPosition(window_, position.x_, position.y_);
  82. else
  83. position_ = position; // Sets as initial position for OpenWindow()
  84. }
  85. void Graphics::SetWindowPosition(int x, int y)
  86. {
  87. SetWindowPosition(IntVector2(x, y));
  88. }
  89. void Graphics::SetOrientations(const String& orientations)
  90. {
  91. orientations_ = orientations.Trimmed();
  92. SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations_.CString());
  93. }
  94. bool Graphics::SetScreenMode(int width, int height)
  95. {
  96. return SetScreenMode(width, height, screenParams_);
  97. }
  98. bool Graphics::SetWindowModes(const WindowModeParams& windowMode, const WindowModeParams& secondaryWindowMode, bool maximize)
  99. {
  100. primaryWindowMode_ = windowMode;
  101. secondaryWindowMode_ = secondaryWindowMode;
  102. return SetScreenMode(primaryWindowMode_.width_, primaryWindowMode_.height_, primaryWindowMode_.screenParams_, maximize);
  103. }
  104. bool Graphics::SetDefaultWindowModes(int width, int height, const ScreenModeParams& params)
  105. {
  106. // Fill window mode to be applied now
  107. WindowModeParams primaryWindowMode;
  108. primaryWindowMode.width_ = width;
  109. primaryWindowMode.height_ = height;
  110. primaryWindowMode.screenParams_ = params;
  111. // Fill window mode to be applied on Graphics::ToggleFullscreen
  112. WindowModeParams secondaryWindowMode = primaryWindowMode;
  113. // Pick resolution automatically
  114. secondaryWindowMode.width_ = 0;
  115. secondaryWindowMode.height_ = 0;
  116. if (params.fullscreen_ || params.borderless_)
  117. {
  118. secondaryWindowMode.screenParams_.fullscreen_ = false;
  119. secondaryWindowMode.screenParams_.borderless_ = false;
  120. }
  121. else
  122. {
  123. secondaryWindowMode.screenParams_.borderless_ = true;
  124. }
  125. const bool maximize = (!width || !height) && !params.fullscreen_ && !params.borderless_ && params.resizable_;
  126. return SetWindowModes(primaryWindowMode, secondaryWindowMode, maximize);
  127. }
  128. bool Graphics::SetMode(int width, int height, bool fullscreen, bool borderless, bool resizable,
  129. bool highDPI, bool vsync, bool tripleBuffer, int multiSample, int monitor, int refreshRate)
  130. {
  131. ScreenModeParams params;
  132. params.fullscreen_ = fullscreen;
  133. params.borderless_ = borderless;
  134. params.resizable_ = resizable;
  135. params.highDPI_ = highDPI;
  136. params.vsync_ = vsync;
  137. params.tripleBuffer_ = tripleBuffer;
  138. params.multiSample_ = multiSample;
  139. params.monitor_ = monitor;
  140. params.refreshRate_ = refreshRate;
  141. return SetDefaultWindowModes(width, height, params);
  142. }
  143. bool Graphics::SetMode(int width, int height)
  144. {
  145. return SetDefaultWindowModes(width, height, screenParams_);
  146. }
  147. bool Graphics::ToggleFullscreen()
  148. {
  149. Swap(primaryWindowMode_, secondaryWindowMode_);
  150. return SetScreenMode(primaryWindowMode_.width_, primaryWindowMode_.height_, primaryWindowMode_.screenParams_);
  151. }
  152. void Graphics::SetShaderParameter(StringHash param, const Variant& value)
  153. {
  154. switch (value.GetType())
  155. {
  156. case VAR_BOOL:
  157. SetShaderParameter(param, value.GetBool());
  158. break;
  159. case VAR_INT:
  160. SetShaderParameter(param, value.GetInt());
  161. break;
  162. case VAR_FLOAT:
  163. case VAR_DOUBLE:
  164. SetShaderParameter(param, value.GetFloat());
  165. break;
  166. case VAR_VECTOR2:
  167. SetShaderParameter(param, value.GetVector2());
  168. break;
  169. case VAR_VECTOR3:
  170. SetShaderParameter(param, value.GetVector3());
  171. break;
  172. case VAR_VECTOR4:
  173. SetShaderParameter(param, value.GetVector4());
  174. break;
  175. case VAR_COLOR:
  176. SetShaderParameter(param, value.GetColor());
  177. break;
  178. case VAR_MATRIX3:
  179. SetShaderParameter(param, value.GetMatrix3());
  180. break;
  181. case VAR_MATRIX3X4:
  182. SetShaderParameter(param, value.GetMatrix3x4());
  183. break;
  184. case VAR_MATRIX4:
  185. SetShaderParameter(param, value.GetMatrix4());
  186. break;
  187. case VAR_BUFFER:
  188. {
  189. const PODVector<unsigned char>& buffer = value.GetBuffer();
  190. if (buffer.Size() >= sizeof(float))
  191. SetShaderParameter(param, reinterpret_cast<const float*>(&buffer[0]), buffer.Size() / sizeof(float));
  192. }
  193. break;
  194. default:
  195. // Unsupported parameter type, do nothing
  196. break;
  197. }
  198. }
  199. IntVector2 Graphics::GetWindowPosition() const
  200. {
  201. if (window_)
  202. {
  203. IntVector2 position;
  204. SDL_GetWindowPosition(window_, &position.x_, &position.y_);
  205. return position;
  206. }
  207. return position_;
  208. }
  209. PODVector<IntVector3> Graphics::GetResolutions(int monitor) const
  210. {
  211. PODVector<IntVector3> ret;
  212. // Emscripten is not able to return a valid list
  213. #ifndef __EMSCRIPTEN__
  214. auto numModes = (unsigned)SDL_GetNumDisplayModes(monitor);
  215. for (unsigned i = 0; i < numModes; ++i)
  216. {
  217. SDL_DisplayMode mode;
  218. SDL_GetDisplayMode(monitor, i, &mode);
  219. int width = mode.w;
  220. int height = mode.h;
  221. int rate = mode.refresh_rate;
  222. // Store mode if unique
  223. bool unique = true;
  224. for (unsigned j = 0; j < ret.Size(); ++j)
  225. {
  226. if (ret[j].x_ == width && ret[j].y_ == height && ret[j].z_ == rate)
  227. {
  228. unique = false;
  229. break;
  230. }
  231. }
  232. if (unique)
  233. ret.Push(IntVector3(width, height, rate));
  234. }
  235. #endif
  236. return ret;
  237. }
  238. unsigned Graphics::FindBestResolutionIndex(int monitor, int width, int height, int refreshRate) const
  239. {
  240. const PODVector<IntVector3> resolutions = GetResolutions(monitor);
  241. if (resolutions.Empty())
  242. return M_MAX_UNSIGNED;
  243. unsigned best = 0;
  244. unsigned bestError = M_MAX_UNSIGNED;
  245. for (unsigned i = 0; i < resolutions.Size(); ++i)
  246. {
  247. auto error = static_cast<unsigned>(Abs(resolutions[i].x_ - width) + Abs(resolutions[i].y_ - height));
  248. if (refreshRate != 0)
  249. error += static_cast<unsigned>(Abs(resolutions[i].z_ - refreshRate));
  250. if (error < bestError)
  251. {
  252. best = i;
  253. bestError = error;
  254. }
  255. }
  256. return best;
  257. }
  258. IntVector2 Graphics::GetDesktopResolution(int monitor) const
  259. {
  260. #if !defined(__ANDROID__) && !defined(IOS) && !defined(TVOS)
  261. SDL_DisplayMode mode;
  262. SDL_GetDesktopDisplayMode(monitor, &mode);
  263. return IntVector2(mode.w, mode.h);
  264. #else
  265. // SDL_GetDesktopDisplayMode() may not work correctly on mobile platforms. Rather return the window size
  266. return IntVector2(width_, height_);
  267. #endif
  268. }
  269. int Graphics::GetMonitorCount() const
  270. {
  271. return SDL_GetNumVideoDisplays();
  272. }
  273. int Graphics::GetCurrentMonitor() const
  274. {
  275. return window_ ? SDL_GetWindowDisplayIndex(window_) : 0;
  276. }
  277. bool Graphics::GetMaximized() const
  278. {
  279. return window_? static_cast<bool>(SDL_GetWindowFlags(window_) & SDL_WINDOW_MAXIMIZED) : false;
  280. }
  281. Vector3 Graphics::GetDisplayDPI(int monitor) const
  282. {
  283. Vector3 result;
  284. SDL_GetDisplayDPI(monitor, &result.z_, &result.x_, &result.y_);
  285. return result;
  286. }
  287. void Graphics::Maximize()
  288. {
  289. if (!window_)
  290. return;
  291. SDL_MaximizeWindow(window_);
  292. }
  293. void Graphics::Minimize()
  294. {
  295. if (!window_)
  296. return;
  297. SDL_MinimizeWindow(window_);
  298. }
  299. void Graphics::Raise() const
  300. {
  301. if (!window_)
  302. return;
  303. SDL_RaiseWindow(window_);
  304. }
  305. void Graphics::BeginDumpShaders(const String& fileName)
  306. {
  307. shaderPrecache_ = new ShaderPrecache(context_, fileName);
  308. }
  309. void Graphics::EndDumpShaders()
  310. {
  311. shaderPrecache_.Reset();
  312. }
  313. void Graphics::PrecacheShaders(Deserializer& source)
  314. {
  315. URHO3D_PROFILE(PrecacheShaders);
  316. ShaderPrecache::LoadShaders(this, source);
  317. }
  318. void Graphics::SetShaderCacheDir(const String& path)
  319. {
  320. String trimmedPath = path.Trimmed();
  321. if (trimmedPath.Length())
  322. shaderCacheDir_ = AddTrailingSlash(trimmedPath);
  323. }
  324. void Graphics::AddGPUObject(GPUObject* object)
  325. {
  326. MutexLock lock(gpuObjectMutex_);
  327. gpuObjects_.Push(object);
  328. }
  329. void Graphics::RemoveGPUObject(GPUObject* object)
  330. {
  331. MutexLock lock(gpuObjectMutex_);
  332. gpuObjects_.Remove(object);
  333. }
  334. void* Graphics::ReserveScratchBuffer(unsigned size)
  335. {
  336. if (!size)
  337. return nullptr;
  338. if (size > maxScratchBufferRequest_)
  339. maxScratchBufferRequest_ = size;
  340. // First check for a free buffer that is large enough
  341. for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
  342. {
  343. if (!i->reserved_ && i->size_ >= size)
  344. {
  345. i->reserved_ = true;
  346. return i->data_.Get();
  347. }
  348. }
  349. // Then check if a free buffer can be resized
  350. for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
  351. {
  352. if (!i->reserved_)
  353. {
  354. i->data_ = new unsigned char[size];
  355. i->size_ = size;
  356. i->reserved_ = true;
  357. URHO3D_LOGDEBUG("Resized scratch buffer to size " + String(size));
  358. return i->data_.Get();
  359. }
  360. }
  361. // Finally allocate a new buffer
  362. ScratchBuffer newBuffer;
  363. newBuffer.data_ = new unsigned char[size];
  364. newBuffer.size_ = size;
  365. newBuffer.reserved_ = true;
  366. scratchBuffers_.Push(newBuffer);
  367. URHO3D_LOGDEBUG("Allocated scratch buffer with size " + String(size));
  368. return newBuffer.data_.Get();
  369. }
  370. void Graphics::FreeScratchBuffer(void* buffer)
  371. {
  372. if (!buffer)
  373. return;
  374. for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
  375. {
  376. if (i->reserved_ && i->data_.Get() == buffer)
  377. {
  378. i->reserved_ = false;
  379. return;
  380. }
  381. }
  382. URHO3D_LOGWARNING("Reserved scratch buffer " + ToStringHex((unsigned)(size_t)buffer) + " not found");
  383. }
  384. void Graphics::CleanupScratchBuffers()
  385. {
  386. for (Vector<ScratchBuffer>::Iterator i = scratchBuffers_.Begin(); i != scratchBuffers_.End(); ++i)
  387. {
  388. if (!i->reserved_ && i->size_ > maxScratchBufferRequest_ * 2 && i->size_ >= 1024 * 1024)
  389. {
  390. i->data_ = maxScratchBufferRequest_ > 0 ? (new unsigned char[maxScratchBufferRequest_]) : nullptr;
  391. i->size_ = maxScratchBufferRequest_;
  392. URHO3D_LOGDEBUG("Resized scratch buffer to size " + String(maxScratchBufferRequest_));
  393. }
  394. }
  395. maxScratchBufferRequest_ = 0;
  396. }
  397. void Graphics::CreateWindowIcon()
  398. {
  399. if (windowIcon_)
  400. {
  401. SDL_Surface* surface = windowIcon_->GetSDLSurface();
  402. if (surface)
  403. {
  404. SDL_SetWindowIcon(window_, surface);
  405. SDL_FreeSurface(surface);
  406. }
  407. }
  408. }
  409. void Graphics::AdjustScreenMode(int& newWidth, int& newHeight, ScreenModeParams& params, bool& maximize) const
  410. {
  411. // High DPI is supported only for OpenGL backend
  412. #ifndef URHO3D_OPENGL
  413. params.highDPI_ = false;
  414. #endif
  415. #if defined(IOS) || defined(TVOS)
  416. // iOS and tvOS app always take the fullscreen (and with status bar hidden)
  417. params.fullscreen_ = true;
  418. #endif
  419. // Make sure monitor index is not bigger than the currently detected monitors
  420. const int numMonitors = SDL_GetNumVideoDisplays();
  421. if (params.monitor_ >= numMonitors || params.monitor_ < 0)
  422. params.monitor_ = 0; // this monitor is not present, use first monitor
  423. // Fullscreen or Borderless can not be resizable and cannot be maximized
  424. if (params.fullscreen_ || params.borderless_)
  425. {
  426. params.resizable_ = false;
  427. maximize = false;
  428. }
  429. // Borderless cannot be fullscreen, they are mutually exclusive
  430. if (params.borderless_)
  431. params.fullscreen_ = false;
  432. // On iOS window needs to be resizable to handle orientation changes properly
  433. #ifdef IOS
  434. if (!externalWindow_)
  435. params.resizable_ = true;
  436. #endif
  437. // Ensure that multisampl factor is in valid range
  438. params.multiSample_ = Clamp(params.multiSample_, 1, 16);
  439. // If zero dimensions in windowed mode, set windowed mode to maximize and set a predefined default restored window size.
  440. // If zero in fullscreen, use desktop mode
  441. if (!newWidth || !newHeight)
  442. {
  443. if (params.fullscreen_ || params.borderless_)
  444. {
  445. SDL_DisplayMode mode;
  446. SDL_GetDesktopDisplayMode(params.monitor_, &mode);
  447. newWidth = mode.w;
  448. newHeight = mode.h;
  449. }
  450. else
  451. {
  452. newWidth = 1024;
  453. newHeight = 768;
  454. }
  455. }
  456. // Check fullscreen mode validity (desktop only). Use a closest match if not found
  457. #ifdef DESKTOP_GRAPHICS
  458. if (params.fullscreen_)
  459. {
  460. const PODVector<IntVector3> resolutions = GetResolutions(params.monitor_);
  461. if (!resolutions.Empty())
  462. {
  463. const unsigned bestResolution = FindBestResolutionIndex(params.monitor_,
  464. newWidth, newHeight, params.refreshRate_);
  465. newWidth = resolutions[bestResolution].x_;
  466. newHeight = resolutions[bestResolution].y_;
  467. params.refreshRate_ = resolutions[bestResolution].z_;
  468. }
  469. }
  470. else
  471. {
  472. // If windowed, use the same refresh rate as desktop
  473. SDL_DisplayMode mode;
  474. SDL_GetDesktopDisplayMode(params.monitor_, &mode);
  475. params.refreshRate_ = mode.refresh_rate;
  476. }
  477. #endif
  478. }
  479. void Graphics::OnScreenModeChanged()
  480. {
  481. #ifdef URHO3D_LOGGING
  482. String msg;
  483. msg.AppendWithFormat("Set screen mode %dx%d rate %d Hz %s monitor %d", width_, height_, screenParams_.refreshRate_,
  484. (screenParams_.fullscreen_ ? "fullscreen" : "windowed"), screenParams_.monitor_);
  485. if (screenParams_.borderless_)
  486. msg.Append(" borderless");
  487. if (screenParams_.resizable_)
  488. msg.Append(" resizable");
  489. if (screenParams_.highDPI_)
  490. msg.Append(" highDPI");
  491. if (screenParams_.multiSample_ > 1)
  492. msg.AppendWithFormat(" multisample %d", screenParams_.multiSample_);
  493. URHO3D_LOGINFO(msg);
  494. #endif
  495. using namespace ScreenMode;
  496. VariantMap& eventData = GetEventDataMap();
  497. eventData[P_WIDTH] = width_;
  498. eventData[P_HEIGHT] = height_;
  499. eventData[P_FULLSCREEN] = screenParams_.fullscreen_;
  500. eventData[P_BORDERLESS] = screenParams_.borderless_;
  501. eventData[P_RESIZABLE] = screenParams_.resizable_;
  502. eventData[P_HIGHDPI] = screenParams_.highDPI_;
  503. eventData[P_MONITOR] = screenParams_.monitor_;
  504. eventData[P_REFRESHRATE] = screenParams_.refreshRate_;
  505. SendEvent(E_SCREENMODE, eventData);
  506. }
  507. void RegisterGraphicsLibrary(Context* context)
  508. {
  509. Animation::RegisterObject(context);
  510. Material::RegisterObject(context);
  511. Model::RegisterObject(context);
  512. Shader::RegisterObject(context);
  513. Technique::RegisterObject(context);
  514. Texture2D::RegisterObject(context);
  515. Texture2DArray::RegisterObject(context);
  516. Texture3D::RegisterObject(context);
  517. TextureCube::RegisterObject(context);
  518. Camera::RegisterObject(context);
  519. Drawable::RegisterObject(context);
  520. Light::RegisterObject(context);
  521. StaticModel::RegisterObject(context);
  522. StaticModelGroup::RegisterObject(context);
  523. Skybox::RegisterObject(context);
  524. AnimatedModel::RegisterObject(context);
  525. AnimationController::RegisterObject(context);
  526. BillboardSet::RegisterObject(context);
  527. ParticleEffect::RegisterObject(context);
  528. ParticleEmitter::RegisterObject(context);
  529. RibbonTrail::RegisterObject(context);
  530. CustomGeometry::RegisterObject(context);
  531. DecalSet::RegisterObject(context);
  532. Terrain::RegisterObject(context);
  533. TerrainPatch::RegisterObject(context);
  534. DebugRenderer::RegisterObject(context);
  535. Octree::RegisterObject(context);
  536. Zone::RegisterObject(context);
  537. }
  538. }