3
0

WindowContext.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  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/RPISystemInterface.h>
  9. #include <Atom/RPI.Public/WindowContext.h>
  10. #include <Atom/RPI.Public/WindowContextBus.h>
  11. #include <Atom/RPI.Public/Pass/PassSystemInterface.h>
  12. #include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
  13. #include <Atom/RHI/Factory.h>
  14. #include <Atom/RHI/RHISystemInterface.h>
  15. #include <AzCore/Console/IConsole.h>
  16. #include <AzCore/Math/MathUtils.h>
  17. namespace AZ
  18. {
  19. namespace RPI
  20. {
  21. void WindowContext::Initialize(RHI::Device& device, AzFramework::NativeWindowHandle windowHandle)
  22. {
  23. m_windowHandle = windowHandle;
  24. if (RHI::CheckBitsAll(device.GetFeatures().m_swapchainScalingFlags, RHI::ScalingFlags::AspectRatioStretch))
  25. {
  26. m_swapChainScalingMode = RHI::Scaling::AspectRatioStretch;
  27. }
  28. else if (RHI::CheckBitsAll(device.GetFeatures().m_swapchainScalingFlags, RHI::ScalingFlags::Stretch))
  29. {
  30. m_swapChainScalingMode = RHI::Scaling::Stretch;
  31. }
  32. else
  33. {
  34. m_swapChainScalingMode = RHI::Scaling::None;
  35. }
  36. CreateSwapChains(device);
  37. AzFramework::WindowNotificationBus::Handler::BusConnect(m_windowHandle);
  38. AzFramework::ExclusiveFullScreenRequestBus::Handler::BusConnect(m_windowHandle);
  39. }
  40. AZStd::vector<ViewportContextPtr> WindowContext::GetAssociatedViewportContexts()
  41. {
  42. AZStd::vector<ViewportContextPtr> associatedContexts;
  43. for (auto viewportContextWeakRef : m_viewportContexts)
  44. {
  45. if (auto viewportContextRef = viewportContextWeakRef.lock())
  46. {
  47. associatedContexts.push_back(viewportContextRef);
  48. }
  49. }
  50. return associatedContexts;
  51. }
  52. void WindowContext::RegisterAssociatedViewportContext(ViewportContextPtr viewportContext)
  53. {
  54. m_viewportContexts.push_back(viewportContext);
  55. }
  56. void WindowContext::Shutdown()
  57. {
  58. AzFramework::ExclusiveFullScreenRequestBus::Handler::BusDisconnect(m_windowHandle);
  59. AzFramework::WindowNotificationBus::Handler::BusDisconnect(m_windowHandle);
  60. DestroyDefaultSwapChain();
  61. DestroyXRSwapChains();
  62. m_swapChainsData.clear();
  63. }
  64. const RHI::AttachmentId& WindowContext::GetSwapChainAttachmentId(ViewType viewType) const
  65. {
  66. return GetSwapChain(viewType)->GetAttachmentId();
  67. }
  68. const RHI::Ptr<RHI::SwapChain>& WindowContext::GetSwapChain(ViewType viewType) const
  69. {
  70. uint32_t swapChainIndex = static_cast<uint32_t>(viewType);
  71. AZ_Assert(swapChainIndex < GetSwapChainsSize(), "Swapchain with index %i does not exist", swapChainIndex);
  72. return m_swapChainsData[swapChainIndex].m_swapChain;
  73. }
  74. uint32_t WindowContext::GetSwapChainsSize() const
  75. {
  76. return aznumeric_cast<uint32_t>(m_swapChainsData.size());
  77. }
  78. RHI::Scaling WindowContext::GetSwapChainScalingMode() const
  79. {
  80. return m_swapChainScalingMode;
  81. }
  82. const RHI::Viewport& WindowContext::GetViewport(ViewType viewType) const
  83. {
  84. uint32_t swapChainIndex = static_cast<uint32_t>(viewType);
  85. AZ_Assert(swapChainIndex < GetSwapChainsSize(), "Swapchain does not exist");
  86. return m_swapChainsData[swapChainIndex].m_viewport;
  87. }
  88. const RHI::Scissor& WindowContext::GetScissor(ViewType viewType) const
  89. {
  90. uint32_t swapChainIndex = static_cast<uint32_t>(viewType);
  91. AZ_Assert(swapChainIndex < GetSwapChainsSize(), "Swapchain does not exist");
  92. return m_swapChainsData[swapChainIndex].m_scissor;
  93. }
  94. void WindowContext::OnWindowResized([[maybe_unused]]uint32_t width, [[maybe_unused]]uint32_t height)
  95. {
  96. CheckResizeSwapChain();
  97. }
  98. void WindowContext::OnResolutionChanged([[maybe_unused]]uint32_t width, [[maybe_unused]]uint32_t height)
  99. {
  100. CheckResizeSwapChain();
  101. }
  102. bool WindowContext::CheckResizeSwapChain()
  103. {
  104. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  105. const AZ::RHI::SwapChainDimensions& currentDimensions = defaultSwapChain->GetDescriptor().m_dimensions;
  106. AzFramework::WindowSize renderSize = ResolveSwapchainSize();
  107. if (renderSize.m_width != currentDimensions.m_imageWidth || renderSize.m_height != currentDimensions.m_imageHeight)
  108. {
  109. // Get current dimension and only overwrite the sizes.
  110. RHI::SwapChainDimensions dimensions = defaultSwapChain->GetDescriptor().m_dimensions;
  111. dimensions.m_imageWidth = renderSize.m_width;
  112. dimensions.m_imageHeight = renderSize.m_height;
  113. dimensions.m_imageFormat = GetSwapChainFormat(defaultSwapChain->GetDevice());
  114. FillWindowState(dimensions.m_imageWidth, dimensions.m_imageHeight);
  115. defaultSwapChain->Resize(dimensions);
  116. WindowContextNotificationBus::Event(m_windowHandle, &WindowContextNotifications::OnViewportResized, dimensions.m_imageWidth, dimensions.m_imageHeight);
  117. return true;
  118. }
  119. return false;
  120. }
  121. void WindowContext::OnWindowClosed()
  122. {
  123. DestroyDefaultSwapChain();
  124. DestroyXRSwapChains();
  125. // We don't want to listen to events anymore if the window has closed
  126. AzFramework::ExclusiveFullScreenRequestBus::Handler::BusDisconnect(m_windowHandle);
  127. AzFramework::WindowNotificationBus::Handler::BusDisconnect(m_windowHandle);
  128. }
  129. void WindowContext::OnVsyncIntervalChanged(uint32_t interval)
  130. {
  131. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  132. if (defaultSwapChain->GetDescriptor().m_verticalSyncInterval != interval)
  133. {
  134. defaultSwapChain->SetVerticalSyncInterval(interval);
  135. }
  136. }
  137. bool WindowContext::IsExclusiveFullScreenPreferred() const
  138. {
  139. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  140. return defaultSwapChain->IsExclusiveFullScreenPreferred();
  141. }
  142. bool WindowContext::GetExclusiveFullScreenState() const
  143. {
  144. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  145. return defaultSwapChain->GetExclusiveFullScreenState();
  146. }
  147. bool WindowContext::SetExclusiveFullScreenState(bool fullScreenState)
  148. {
  149. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  150. return defaultSwapChain->SetExclusiveFullScreenState(fullScreenState);
  151. }
  152. AzFramework::WindowSize WindowContext::ResolveSwapchainSize()
  153. {
  154. AzFramework::WindowSize windowSize;
  155. AzFramework::WindowSize renderSize;
  156. AzFramework::WindowRequestBus::EventResult(
  157. windowSize,
  158. m_windowHandle,
  159. &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
  160. AzFramework::WindowRequestBus::EventResult(
  161. renderSize,
  162. m_windowHandle,
  163. &AzFramework::WindowRequestBus::Events::GetRenderResolution);
  164. if (windowSize != renderSize && m_swapChainScalingMode == RHI::Scaling::None)
  165. {
  166. // no stretch support. we need to use the window size for render size
  167. renderSize = windowSize;
  168. }
  169. renderSize.m_width = AZStd::max(renderSize.m_width, 1u);
  170. renderSize.m_height = AZStd::max(renderSize.m_height, 1u);
  171. return renderSize;
  172. }
  173. void WindowContext::CreateSwapChains(RHI::Device& device)
  174. {
  175. RHI::Ptr<RHI::SwapChain> swapChain = RHI::Factory::Get().CreateSwapChain();
  176. RHI::SwapChainDescriptor descriptor;
  177. AzFramework::WindowSize renderSize = ResolveSwapchainSize();
  178. const RHI::WindowHandle windowHandle = RHI::WindowHandle(reinterpret_cast<uintptr_t>(m_windowHandle));
  179. uint32_t syncInterval = 1;
  180. AzFramework::WindowRequestBus::EventResult(
  181. syncInterval, m_windowHandle, &AzFramework::WindowRequestBus::Events::GetSyncInterval);
  182. descriptor.m_window = windowHandle;
  183. descriptor.m_verticalSyncInterval = syncInterval;
  184. descriptor.m_dimensions.m_imageWidth = renderSize.m_width;
  185. descriptor.m_dimensions.m_imageHeight = renderSize.m_height;
  186. descriptor.m_dimensions.m_imageCount = AZStd::max(RHI::Limits::Device::MinSwapChainImages, RHI::Limits::Device::FrameCountMax);
  187. descriptor.m_dimensions.m_imageFormat = GetSwapChainFormat(device);
  188. descriptor.m_scalingMode = m_swapChainScalingMode;
  189. AZStd::string attachmentName = AZStd::string::format("WindowContextAttachment_%p", m_windowHandle);
  190. descriptor.m_attachmentId = RHI::AttachmentId{ attachmentName.c_str() };
  191. swapChain->Init(device, descriptor);
  192. descriptor = swapChain->GetDescriptor(); // Get descriptor from swapchain because it can set different values during initialization
  193. RHI::Viewport viewport;
  194. viewport.m_maxX = static_cast<float>(descriptor.m_dimensions.m_imageWidth);
  195. viewport.m_maxY = static_cast<float>(descriptor.m_dimensions.m_imageHeight);
  196. RHI::Scissor scissor;
  197. scissor.m_maxX = static_cast<int16_t>(descriptor.m_dimensions.m_imageWidth);
  198. scissor.m_maxY = static_cast<int16_t>(descriptor.m_dimensions.m_imageHeight);
  199. uint32_t defaultSwapChainIndex = static_cast<uint32_t>(DefaultViewType);
  200. if (defaultSwapChainIndex < m_swapChainsData.size())
  201. {
  202. m_swapChainsData[defaultSwapChainIndex].m_swapChain = swapChain;
  203. m_swapChainsData[defaultSwapChainIndex].m_viewport = viewport;
  204. m_swapChainsData[defaultSwapChainIndex].m_scissor = scissor;
  205. }
  206. else
  207. {
  208. m_swapChainsData.insert(
  209. m_swapChainsData.begin() + defaultSwapChainIndex, SwapChainData{ swapChain, viewport, scissor });
  210. }
  211. // Add XR pipelines if it is active
  212. XRRenderingInterface* xrSystem = RPISystemInterface::Get()->GetXRSystem();
  213. if (xrSystem)
  214. {
  215. const AZ::u32 numXrViews = xrSystem->GetNumViews();
  216. AZ_Assert(numXrViews <= 2, "Atom only supports two XR views");
  217. for (AZ::u32 i = 0; i < numXrViews; i++)
  218. {
  219. RHI::Ptr<RHI::SwapChain> xrSwapChain = RHI::Factory::Get().CreateSwapChain();
  220. RHI::SwapChainDescriptor xrDescriptor;
  221. xrDescriptor.m_dimensions.m_imageWidth = xrSystem->GetSwapChainWidth(i);
  222. xrDescriptor.m_dimensions.m_imageHeight = xrSystem->GetSwapChainHeight(i);
  223. xrDescriptor.m_dimensions.m_imageCount = AZ::RHI::Limits::Device::FrameCountMax;
  224. xrDescriptor.m_isXrSwapChain = true;
  225. xrDescriptor.m_xrSwapChainIndex = i;
  226. xrDescriptor.m_dimensions.m_imageFormat = xrSystem->GetSwapChainFormat(i);
  227. xrDescriptor.m_scalingMode = m_swapChainScalingMode;
  228. const AZStd::string xrAttachmentName = AZStd::string::format("XRSwapChain_View_%i", i);
  229. xrDescriptor.m_attachmentId = RHI::AttachmentId{ xrAttachmentName.c_str() };
  230. xrSwapChain->Init(device, xrDescriptor);
  231. xrDescriptor = xrSwapChain->GetDescriptor(); // Get descriptor from swapchain because it can set different values during initialization
  232. RHI::Viewport xrViewport;
  233. xrViewport.m_maxX = static_cast<float>(xrDescriptor.m_dimensions.m_imageWidth);
  234. xrViewport.m_maxY = static_cast<float>(xrDescriptor.m_dimensions.m_imageHeight);
  235. RHI::Scissor xrScissor;
  236. xrScissor.m_maxX = static_cast<int16_t>(xrDescriptor.m_dimensions.m_imageWidth);
  237. xrScissor.m_maxY = static_cast<int16_t>(xrDescriptor.m_dimensions.m_imageHeight);
  238. uint32_t xrSwapChainIndex = i == 0 ? static_cast<uint32_t>(ViewType::XrLeft) : static_cast<uint32_t>(ViewType::XrRight);
  239. if (xrSwapChainIndex < m_swapChainsData.size())
  240. {
  241. m_swapChainsData[xrSwapChainIndex].m_swapChain = xrSwapChain;
  242. m_swapChainsData[xrSwapChainIndex].m_viewport = xrViewport;
  243. m_swapChainsData[xrSwapChainIndex].m_scissor = xrScissor;
  244. }
  245. else
  246. {
  247. m_swapChainsData.insert(m_swapChainsData.begin() + xrSwapChainIndex, SwapChainData{xrSwapChain, xrViewport, xrScissor});
  248. }
  249. }
  250. }
  251. }
  252. void WindowContext::DestroyDefaultSwapChain()
  253. {
  254. DestroySwapChain(DefaultViewType);
  255. }
  256. void WindowContext::DestroyXRSwapChains()
  257. {
  258. DestroySwapChain(static_cast<uint32_t>(ViewType::XrLeft));
  259. DestroySwapChain(static_cast<uint32_t>(ViewType::XrRight));
  260. }
  261. void WindowContext::DestroySwapChain(uint32_t swapChainIndex)
  262. {
  263. if (swapChainIndex < m_swapChainsData.size())
  264. {
  265. m_swapChainsData[swapChainIndex].m_swapChain = nullptr;
  266. }
  267. }
  268. void WindowContext::FillWindowState(const uint32_t width, const uint32_t height)
  269. {
  270. auto& defaultViewport = m_swapChainsData[DefaultViewType].m_viewport;
  271. auto& defaultScissor = m_swapChainsData[DefaultViewType].m_scissor;
  272. defaultViewport.m_minX = 0;
  273. defaultViewport.m_minY = 0;
  274. defaultViewport.m_maxX = static_cast<float>(width);
  275. defaultViewport.m_maxY = static_cast<float>(height);
  276. defaultScissor.m_minX = 0;
  277. defaultScissor.m_minY = 0;
  278. defaultScissor.m_maxX = static_cast<int16_t>(width);
  279. defaultScissor.m_maxY = static_cast<int16_t>(height);
  280. }
  281. RHI::Format WindowContext::GetSwapChainFormat(RHI::Device& device) const
  282. {
  283. // Array of preferred format in decreasing order of preference.
  284. const AZStd::array<RHI::Format, 3> preferredFormats =
  285. {{
  286. RHI::Format::R10G10B10A2_UNORM,
  287. RHI::Format::R8G8B8A8_UNORM,
  288. RHI::Format::B8G8R8A8_UNORM
  289. }};
  290. auto GetPreferredFormat = [](const AZStd::array<RHI::Format, 3>& preferredFormats, const AZStd::vector<RHI::Format>& supportedFormats) -> RHI::Format
  291. {
  292. for (int preferredIndex = 0; preferredIndex < preferredFormats.size(); ++preferredIndex)
  293. {
  294. for (int supportedIndex = 0; supportedIndex < supportedFormats.size(); ++supportedIndex)
  295. {
  296. if (supportedFormats[supportedIndex] == preferredFormats[preferredIndex])
  297. {
  298. return supportedFormats[supportedIndex];
  299. }
  300. }
  301. }
  302. // If no match found, just return the first supported format
  303. return supportedFormats[0];
  304. };
  305. const RHI::WindowHandle windowHandle = RHI::WindowHandle(reinterpret_cast<uintptr_t>(m_windowHandle));
  306. const AZStd::vector<RHI::Format> supportedFormats = device.GetValidSwapChainImageFormats(windowHandle);
  307. AZ_Assert(!supportedFormats.empty(), "There is no supported format for SwapChain images.");
  308. return GetPreferredFormat(preferredFormats, supportedFormats);
  309. }
  310. } // namespace RPI
  311. } // namespace AZ