3
0

ViewportContextManager.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Atom/RPI.Public/ViewportContextManager.h>
  9. #include <Atom/RPI.Public/ViewportContext.h>
  10. #include <Atom/RPI.Public/View.h>
  11. namespace AZ
  12. {
  13. namespace RPI
  14. {
  15. static constexpr const char* s_defaultViewportContextName = "Default ViewportContext";
  16. ViewportContextManager::ViewportContextManager()
  17. {
  18. AZ::Interface<ViewportContextRequestsInterface>::Register(this);
  19. m_defaultViewportContextName = AZ::Name(s_defaultViewportContextName);
  20. }
  21. ViewportContextManager::~ViewportContextManager()
  22. {
  23. AZ::Interface<ViewportContextRequestsInterface>::Unregister(this);
  24. }
  25. void ViewportContextManager::Shutdown()
  26. {
  27. m_viewportContexts.clear();
  28. m_viewportViews.clear();
  29. }
  30. void ViewportContextManager::RegisterViewportContext(const Name& contextName, ViewportContextPtr viewportContext)
  31. {
  32. {
  33. AZStd::lock_guard lock(m_containerMutex);
  34. AZ_Assert(viewportContext != nullptr, "Attempted to register a null ViewportContext \"%s\"", contextName.GetCStr());
  35. if (!viewportContext)
  36. {
  37. return;
  38. }
  39. // Create a context data entry, ensure there wasn't a still-registered existing one
  40. const auto viewportId = viewportContext->GetId();
  41. auto& viewportData = m_viewportContexts[viewportId];
  42. AZ_Assert(viewportData.context.expired(), "Attempted multiple registration for ViewportContext \"%s\" detected, please ensure you call IViewportContextManager::UnregisterViewportContext", contextName.GetCStr());
  43. if (!viewportData.context.expired())
  44. {
  45. return;
  46. }
  47. viewportData.context = viewportContext;
  48. auto onSizeChanged = [this, viewportId](AzFramework::WindowSize size)
  49. {
  50. // Ensure we emit OnViewportSizeChanged with the correct name.
  51. auto viewportContext = GetViewportContextById(viewportId);
  52. if (viewportContext)
  53. {
  54. ViewportContextNotificationBus::Event(viewportContext->GetName(), &ViewportContextNotificationBus::Events::OnViewportSizeChanged, size);
  55. }
  56. ViewportContextIdNotificationBus::Event(viewportId, &ViewportContextIdNotificationBus::Events::OnViewportSizeChanged, size);
  57. };
  58. auto onDpiScalingChanged = [this, viewportId](float dpiScalingFactor)
  59. {
  60. // Ensure we emit OnViewportDpiScalingChanged with the correct name.
  61. auto viewportContext = GetViewportContextById(viewportId);
  62. if (viewportContext)
  63. {
  64. ViewportContextNotificationBus::Event(viewportContext->GetName(), &ViewportContextNotificationBus::Events::OnViewportDpiScalingChanged, dpiScalingFactor);
  65. }
  66. ViewportContextIdNotificationBus::Event(viewportId, &ViewportContextIdNotificationBus::Events::OnViewportDpiScalingChanged, dpiScalingFactor);
  67. };
  68. viewportContext->m_name = contextName;
  69. viewportData.sizeChangedHandler = ViewportContext::SizeChangedEvent::Handler(onSizeChanged);
  70. viewportData.dpiScalingChangedHandler = ViewportContext::ScalarChangedEvent::Handler(onDpiScalingChanged);
  71. viewportContext->ConnectSizeChangedHandler(viewportData.sizeChangedHandler);
  72. viewportContext->ConnectDpiScalingFactorChangedHandler(viewportData.dpiScalingChangedHandler);
  73. ViewPtrStack& associatedViews = GetOrCreateViewStackForContext(contextName);
  74. viewportContext->SetViewGroup(associatedViews.back());
  75. onSizeChanged(viewportContext->GetViewportSize());
  76. }
  77. ViewportContextManagerNotificationsBus::Broadcast(&ViewportContextManagerNotifications::OnViewportContextAdded, viewportContext);
  78. }
  79. void ViewportContextManager::UnregisterViewportContext(AzFramework::ViewportId viewportId)
  80. {
  81. {
  82. AZStd::lock_guard lock(m_containerMutex);
  83. AZ_Assert(viewportId != AzFramework::InvalidViewportId, "Attempted to unregister an invalid viewport");
  84. auto viewportContextIt = m_viewportContexts.find(viewportId);
  85. if (viewportContextIt == m_viewportContexts.end())
  86. {
  87. AZ_Assert(false, "Attempted to unregister a ViewportContext \"%i\" when none is registered", viewportId);
  88. return;
  89. }
  90. m_viewportContexts.erase(viewportContextIt);
  91. }
  92. ViewportContextManagerNotificationsBus::Broadcast(&ViewportContextManagerNotifications::OnViewportContextRemoved, viewportId);
  93. }
  94. AzFramework::ViewportId ViewportContextManager::GetViewportIdFromName(const Name& contextName) const
  95. {
  96. AZStd::lock_guard lock(m_containerMutex);
  97. for (const auto& viewportData : m_viewportContexts)
  98. {
  99. auto viewportContextRef = viewportData.second.context;
  100. if (!viewportContextRef.expired() && viewportContextRef.lock()->GetName() == contextName)
  101. {
  102. return viewportData.first;
  103. }
  104. }
  105. return AzFramework::InvalidViewportId;
  106. }
  107. ViewportContextPtr ViewportContextManager::CreateViewportContext(const Name& contextName, const CreationParameters& params)
  108. {
  109. AzFramework::ViewportId id = params.id;
  110. if (id != AzFramework::InvalidViewportId)
  111. {
  112. if (GetViewportContextById(id))
  113. {
  114. AZ_Assert(false, "Attempted to register multiple ViewportContexts to ID %i", id);
  115. return nullptr;
  116. }
  117. }
  118. else
  119. {
  120. // Find the first available ID
  121. do
  122. {
  123. id = m_currentViewportId.fetch_add(1);
  124. } while (GetViewportContextById(id));
  125. }
  126. // Dynamically generate a context name if one isn't provided
  127. Name nameToUse = contextName;
  128. if (nameToUse.IsEmpty())
  129. {
  130. nameToUse = Name(AZStd::string::format("ViewportContext%i", id));
  131. }
  132. AZ_Assert(params.device, "Invalid device provided to CreateViewportContext");
  133. ViewportContextPtr viewportContext = AZStd::make_shared<ViewportContext>(
  134. this,
  135. id,
  136. nameToUse,
  137. *params.device,
  138. params.windowHandle,
  139. params.renderScene
  140. );
  141. viewportContext->GetWindowContext()->RegisterAssociatedViewportContext(viewportContext);
  142. RegisterViewportContext(nameToUse, viewportContext);
  143. return viewportContext;
  144. }
  145. ViewportContextPtr ViewportContextManager::GetViewportContextByName(const Name& contextName) const
  146. {
  147. return GetViewportContextById(GetViewportIdFromName(contextName));
  148. }
  149. ViewportContextPtr ViewportContextManager::GetViewportContextById(AzFramework::ViewportId id) const
  150. {
  151. AZStd::lock_guard lock(m_containerMutex);
  152. if (auto viewportIt = m_viewportContexts.find(id); viewportIt != m_viewportContexts.end())
  153. {
  154. return viewportIt->second.context.lock();
  155. }
  156. return {};
  157. }
  158. ViewportContextPtr ViewportContextManager::GetViewportContextByScene(const Scene* scene) const
  159. {
  160. AZStd::lock_guard lock(m_containerMutex);
  161. for (const auto& viewportData : m_viewportContexts)
  162. {
  163. ViewportContextPtr viewportContext = viewportData.second.context.lock();
  164. if (viewportContext && viewportContext->GetRenderScene().get() == scene)
  165. {
  166. return viewportContext;
  167. }
  168. }
  169. return {};
  170. }
  171. void ViewportContextManager::RenameViewportContext(ViewportContextPtr viewportContext, const Name& newContextName)
  172. {
  173. auto currentAssignedViewportContext = GetViewportContextByName(newContextName);
  174. if (currentAssignedViewportContext)
  175. {
  176. AZ_Assert(false, "Attempted to rename ViewportContext \"%s\" to \"%s\", but \"%s\" is already assigned to another ViewportContext", viewportContext->m_name.GetCStr(), newContextName.GetCStr(), newContextName.GetCStr());
  177. return;
  178. }
  179. // find the existing view group stack with the old name and extract it
  180. auto nodeHandle = m_viewportViews.extract(viewportContext->GetName());
  181. // rename the node handle with the new name (key)
  182. nodeHandle.key() = newContextName;
  183. // insert the updated view group back into the map with the new name
  184. m_viewportViews.insert(AZStd::move(nodeHandle));
  185. // update name of element
  186. viewportContext->m_name = newContextName;
  187. UpdateViewForContext(newContextName);
  188. // Ensure anyone listening on per-name viewport size updates gets notified.
  189. ViewportContextNotificationBus::Event(newContextName, &ViewportContextNotificationBus::Events::OnViewportSizeChanged, viewportContext->GetViewportSize());
  190. ViewportContextNotificationBus::Event(newContextName, &ViewportContextNotificationBus::Events::OnViewportDpiScalingChanged, viewportContext->GetDpiScalingFactor());
  191. }
  192. void ViewportContextManager::EnumerateViewportContexts(AZStd::function<void(ViewportContextPtr)> visitorFunction)
  193. {
  194. AZStd::lock_guard lock(m_containerMutex);
  195. for (const auto& viewportData : m_viewportContexts)
  196. {
  197. visitorFunction(viewportData.second.context.lock());
  198. }
  199. }
  200. AZ::Name ViewportContextManager::GetDefaultViewportContextName() const
  201. {
  202. return m_defaultViewportContextName;
  203. }
  204. ViewportContextPtr ViewportContextManager::GetDefaultViewportContext() const
  205. {
  206. return GetViewportContextByName(m_defaultViewportContextName);
  207. }
  208. void ViewportContextManager::PushViewGroup(const Name& contextName, ViewGroupPtr viewGroup)
  209. {
  210. {
  211. AZStd::lock_guard lock(m_containerMutex);
  212. AZ_Assert(viewGroup->GetNumViews() > 0, "Attempted to push a null view to context \"%s\"", contextName.GetCStr());
  213. ViewPtrStack& associatedViews = GetOrCreateViewStackForContext(contextName);
  214. // Remove from its existing position, if any, before re-adding below at the top of the stack
  215. AZStd::erase(associatedViews, viewGroup);
  216. associatedViews.push_back(viewGroup);
  217. }
  218. UpdateViewForContext(contextName);
  219. }
  220. bool ViewportContextManager::PopViewGroup(const Name& contextName, ViewGroupPtr viewGroup)
  221. {
  222. {
  223. AZStd::lock_guard lock(m_containerMutex);
  224. auto viewStackIt = m_viewportViews.find(contextName);
  225. if (viewStackIt == m_viewportViews.end())
  226. {
  227. return false;
  228. }
  229. ViewPtrStack& associatedViews = viewStackIt->second;
  230. AZ_Assert(!associatedViews.empty(), "There are no associated views for context %s", contextName.GetCStr());
  231. if (viewGroup == associatedViews[0])
  232. {
  233. AZ_Error("ViewportContextManager", false, "Attempted to pop the root view for context \"%s\"", contextName.GetCStr());
  234. return false;
  235. }
  236. // Remove the view group
  237. const size_t eraseCount = AZStd::erase(associatedViews, viewGroup);
  238. if (eraseCount == 0)
  239. {
  240. return false;
  241. }
  242. }
  243. UpdateViewForContext(contextName);
  244. return true;
  245. }
  246. ViewPtr ViewportContextManager::GetCurrentView(const Name& context)
  247. {
  248. AZStd::lock_guard lock(m_containerMutex);
  249. if (auto viewIt = m_viewportViews.find(context); viewIt != m_viewportViews.end())
  250. {
  251. return viewIt->second.back()->GetView(ViewType::Default);
  252. }
  253. return {};
  254. }
  255. ViewGroupPtr ViewportContextManager::GetCurrentViewGroup(const Name& contextName)
  256. {
  257. AZStd::lock_guard lock(m_containerMutex);
  258. if (auto viewIt = m_viewportViews.find(contextName); viewIt != m_viewportViews.end())
  259. {
  260. return viewIt->second.back();
  261. }
  262. return {};
  263. }
  264. ViewPtr ViewportContextManager::GetCurrentStereoscopicView(const Name& context, ViewType viewType)
  265. {
  266. AZStd::lock_guard lock(m_containerMutex);
  267. if (auto viewIt = m_viewportViews.find(context); viewIt != m_viewportViews.end())
  268. {
  269. uint32_t xrViewIndex = static_cast<uint32_t>(viewType);
  270. if (xrViewIndex < viewIt->second.back()->GetNumViews())
  271. {
  272. return viewIt->second.back()->GetView(static_cast<ViewType>(xrViewIndex));
  273. }
  274. }
  275. return {};
  276. }
  277. ViewportContextManager::ViewPtrStack& ViewportContextManager::GetOrCreateViewStackForContext(const Name& context)
  278. {
  279. // If a stack doesn't already exist, create one now to populate it
  280. ViewPtrStack& viewStack = m_viewportViews[context];
  281. if (viewStack.empty())
  282. {
  283. Name defaultViewName = Name(AZStd::string::format("%s (Root Camera)", context.GetCStr()));
  284. ViewGroupPtr defaultViewGroup = AZStd::make_shared<ViewGroup>();
  285. defaultViewGroup->Init(ViewGroup::Descriptor{ nullptr, nullptr });
  286. defaultViewGroup->CreateMainView(defaultViewName);
  287. defaultViewGroup->CreateStereoscopicViews(defaultViewName);
  288. viewStack.push_back(defaultViewGroup);
  289. }
  290. return viewStack;
  291. }
  292. void ViewportContextManager::UpdateViewForContext(const Name& context)
  293. {
  294. auto currentViewGroup = GetCurrentViewGroup(context);
  295. for (const auto& viewportData : m_viewportContexts)
  296. {
  297. ViewportContextPtr viewportContext = viewportData.second.context.lock();
  298. if (viewportContext && viewportContext->GetName() == context)
  299. {
  300. viewportContext->SetViewGroup(currentViewGroup);
  301. ViewportContextIdNotificationBus::Event(
  302. viewportContext->GetId(),
  303. &ViewportContextIdNotificationBus::Events::OnViewportDefaultViewChanged,
  304. currentViewGroup->GetView(ViewType::Default));
  305. break;
  306. }
  307. }
  308. ViewportContextNotificationBus::Event(
  309. context,
  310. &ViewportContextNotificationBus::Events::OnViewportDefaultViewChanged,
  311. currentViewGroup->GetView(ViewType::Default));
  312. }
  313. } // namespace RPI
  314. } // namespace AZ