WindowContext.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  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. // Note: there is only one bit set in the mask, so we simply get the device index with log2
  114. dimensions.m_imageFormat = GetSwapChainFormat(*RHI::RHISystemInterface::Get()->GetDevice(static_cast<int>(log2(static_cast<float>(defaultSwapChain->GetDeviceMask())))));
  115. FillWindowState(dimensions.m_imageWidth, dimensions.m_imageHeight);
  116. defaultSwapChain->Resize(dimensions);
  117. WindowContextNotificationBus::Event(m_windowHandle, &WindowContextNotifications::OnViewportResized, dimensions.m_imageWidth, dimensions.m_imageHeight);
  118. return true;
  119. }
  120. return false;
  121. }
  122. void WindowContext::OnWindowClosed()
  123. {
  124. DestroyDefaultSwapChain();
  125. DestroyXRSwapChains();
  126. // We don't want to listen to events anymore if the window has closed
  127. AzFramework::ExclusiveFullScreenRequestBus::Handler::BusDisconnect(m_windowHandle);
  128. AzFramework::WindowNotificationBus::Handler::BusDisconnect(m_windowHandle);
  129. }
  130. void WindowContext::OnVsyncIntervalChanged(uint32_t interval)
  131. {
  132. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  133. if (defaultSwapChain->GetDescriptor().m_verticalSyncInterval != interval)
  134. {
  135. defaultSwapChain->SetVerticalSyncInterval(interval);
  136. }
  137. }
  138. bool WindowContext::IsExclusiveFullScreenPreferred() const
  139. {
  140. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  141. return defaultSwapChain->IsExclusiveFullScreenPreferred();
  142. }
  143. bool WindowContext::GetExclusiveFullScreenState() const
  144. {
  145. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  146. return defaultSwapChain->GetExclusiveFullScreenState();
  147. }
  148. bool WindowContext::SetExclusiveFullScreenState(bool fullScreenState)
  149. {
  150. RHI::Ptr<RHI::SwapChain> defaultSwapChain = GetSwapChain(ViewType::Default);
  151. return defaultSwapChain->SetExclusiveFullScreenState(fullScreenState);
  152. }
  153. AzFramework::WindowSize WindowContext::ResolveSwapchainSize()
  154. {
  155. AzFramework::WindowSize windowSize;
  156. AzFramework::WindowSize renderSize;
  157. AzFramework::WindowRequestBus::EventResult(
  158. windowSize,
  159. m_windowHandle,
  160. &AzFramework::WindowRequestBus::Events::GetClientAreaSize);
  161. AzFramework::WindowRequestBus::EventResult(
  162. renderSize,
  163. m_windowHandle,
  164. &AzFramework::WindowRequestBus::Events::GetRenderResolution);
  165. if (windowSize != renderSize && m_swapChainScalingMode == RHI::Scaling::None)
  166. {
  167. // no stretch support. we need to use the window size for render size
  168. renderSize = windowSize;
  169. }
  170. renderSize.m_width = AZStd::max(renderSize.m_width, 1u);
  171. renderSize.m_height = AZStd::max(renderSize.m_height, 1u);
  172. return renderSize;
  173. }
  174. void WindowContext::CreateSwapChains(RHI::Device& device)
  175. {
  176. RHI::Ptr<RHI::SwapChain> swapChain = aznew RHI::SwapChain;
  177. RHI::SwapChainDescriptor descriptor;
  178. AzFramework::WindowSize renderSize = ResolveSwapchainSize();
  179. const RHI::WindowHandle windowHandle = RHI::WindowHandle(reinterpret_cast<uintptr_t>(m_windowHandle));
  180. uint32_t syncInterval = 1;
  181. AzFramework::WindowRequestBus::EventResult(
  182. syncInterval, m_windowHandle, &AzFramework::WindowRequestBus::Events::GetSyncInterval);
  183. descriptor.m_window = windowHandle;
  184. descriptor.m_verticalSyncInterval = syncInterval;
  185. descriptor.m_dimensions.m_imageWidth = renderSize.m_width;
  186. descriptor.m_dimensions.m_imageHeight = renderSize.m_height;
  187. descriptor.m_dimensions.m_imageCount = AZStd::max(RHI::Limits::Device::MinSwapChainImages, RHI::Limits::Device::FrameCountMax);
  188. descriptor.m_dimensions.m_imageFormat = GetSwapChainFormat(device);
  189. descriptor.m_scalingMode = m_swapChainScalingMode;
  190. AZStd::string attachmentName = AZStd::string::format("WindowContextAttachment_%p", m_windowHandle);
  191. descriptor.m_attachmentId = RHI::AttachmentId{ attachmentName.c_str() };
  192. swapChain->Init(device.GetDeviceIndex(), descriptor);
  193. descriptor = swapChain->GetDescriptor(); // Get descriptor from swapchain because it can set different values during initialization
  194. RHI::Viewport viewport;
  195. viewport.m_maxX = static_cast<float>(descriptor.m_dimensions.m_imageWidth);
  196. viewport.m_maxY = static_cast<float>(descriptor.m_dimensions.m_imageHeight);
  197. RHI::Scissor scissor;
  198. scissor.m_maxX = static_cast<int16_t>(descriptor.m_dimensions.m_imageWidth);
  199. scissor.m_maxY = static_cast<int16_t>(descriptor.m_dimensions.m_imageHeight);
  200. uint32_t defaultSwapChainIndex = static_cast<uint32_t>(DefaultViewType);
  201. if (defaultSwapChainIndex < m_swapChainsData.size())
  202. {
  203. m_swapChainsData[defaultSwapChainIndex].m_swapChain = swapChain;
  204. m_swapChainsData[defaultSwapChainIndex].m_viewport = viewport;
  205. m_swapChainsData[defaultSwapChainIndex].m_scissor = scissor;
  206. }
  207. else
  208. {
  209. m_swapChainsData.insert(
  210. m_swapChainsData.begin() + defaultSwapChainIndex, SwapChainData{ swapChain, viewport, scissor });
  211. }
  212. // Add XR pipelines if it is active
  213. XRRenderingInterface* xrSystem = RPISystemInterface::Get()->GetXRSystem();
  214. if (xrSystem)
  215. {
  216. const AZ::u32 numXrViews = xrSystem->GetNumViews();
  217. AZ_Assert(numXrViews <= 2, "Atom only supports two XR views");
  218. for (AZ::u32 i = 0; i < numXrViews; i++)
  219. {
  220. RHI::Ptr<RHI::SwapChain> xrSwapChain = aznew RHI::SwapChain;
  221. RHI::SwapChainDescriptor xrDescriptor;
  222. xrDescriptor.m_dimensions.m_imageWidth = xrSystem->GetSwapChainWidth(i);
  223. xrDescriptor.m_dimensions.m_imageHeight = xrSystem->GetSwapChainHeight(i);
  224. xrDescriptor.m_dimensions.m_imageCount = AZ::RHI::Limits::Device::FrameCountMax;
  225. xrDescriptor.m_isXrSwapChain = true;
  226. xrDescriptor.m_xrSwapChainIndex = i;
  227. xrDescriptor.m_dimensions.m_imageFormat = xrSystem->GetSwapChainFormat(i);
  228. xrDescriptor.m_scalingMode = m_swapChainScalingMode;
  229. const AZStd::string xrAttachmentName = AZStd::string::format("XRSwapChain_View_%i", i);
  230. xrDescriptor.m_attachmentId = RHI::AttachmentId{ xrAttachmentName.c_str() };
  231. xrSwapChain->Init(device.GetDeviceIndex(), xrDescriptor);
  232. xrDescriptor = xrSwapChain->GetDescriptor(); // Get descriptor from swapchain because it can set different values during initialization
  233. RHI::Viewport xrViewport;
  234. xrViewport.m_maxX = static_cast<float>(xrDescriptor.m_dimensions.m_imageWidth);
  235. xrViewport.m_maxY = static_cast<float>(xrDescriptor.m_dimensions.m_imageHeight);
  236. RHI::Scissor xrScissor;
  237. xrScissor.m_maxX = static_cast<int16_t>(xrDescriptor.m_dimensions.m_imageWidth);
  238. xrScissor.m_maxY = static_cast<int16_t>(xrDescriptor.m_dimensions.m_imageHeight);
  239. uint32_t xrSwapChainIndex = i == 0 ? static_cast<uint32_t>(ViewType::XrLeft) : static_cast<uint32_t>(ViewType::XrRight);
  240. if (xrSwapChainIndex < m_swapChainsData.size())
  241. {
  242. m_swapChainsData[xrSwapChainIndex].m_swapChain = xrSwapChain;
  243. m_swapChainsData[xrSwapChainIndex].m_viewport = xrViewport;
  244. m_swapChainsData[xrSwapChainIndex].m_scissor = xrScissor;
  245. }
  246. else
  247. {
  248. m_swapChainsData.insert(m_swapChainsData.begin() + xrSwapChainIndex, SwapChainData{xrSwapChain, xrViewport, xrScissor});
  249. }
  250. }
  251. }
  252. }
  253. void WindowContext::DestroyDefaultSwapChain()
  254. {
  255. DestroySwapChain(DefaultViewType);
  256. }
  257. void WindowContext::DestroyXRSwapChains()
  258. {
  259. DestroySwapChain(static_cast<uint32_t>(ViewType::XrLeft));
  260. DestroySwapChain(static_cast<uint32_t>(ViewType::XrRight));
  261. }
  262. void WindowContext::DestroySwapChain(uint32_t swapChainIndex)
  263. {
  264. if (swapChainIndex < m_swapChainsData.size())
  265. {
  266. m_swapChainsData[swapChainIndex].m_swapChain = nullptr;
  267. }
  268. }
  269. void WindowContext::FillWindowState(const uint32_t width, const uint32_t height)
  270. {
  271. auto& defaultViewport = m_swapChainsData[DefaultViewType].m_viewport;
  272. auto& defaultScissor = m_swapChainsData[DefaultViewType].m_scissor;
  273. defaultViewport.m_minX = 0;
  274. defaultViewport.m_minY = 0;
  275. defaultViewport.m_maxX = static_cast<float>(width);
  276. defaultViewport.m_maxY = static_cast<float>(height);
  277. defaultScissor.m_minX = 0;
  278. defaultScissor.m_minY = 0;
  279. defaultScissor.m_maxX = static_cast<int16_t>(width);
  280. defaultScissor.m_maxY = static_cast<int16_t>(height);
  281. }
  282. RHI::Format WindowContext::GetSwapChainFormat(RHI::Device& device) const
  283. {
  284. // Array of preferred format in decreasing order of preference.
  285. const AZStd::array<RHI::Format, 3> preferredFormats =
  286. {{
  287. RHI::Format::R10G10B10A2_UNORM,
  288. RHI::Format::R8G8B8A8_UNORM,
  289. RHI::Format::B8G8R8A8_UNORM
  290. }};
  291. auto GetPreferredFormat = [](const AZStd::array<RHI::Format, 3>& preferredFormats, const AZStd::vector<RHI::Format>& supportedFormats) -> RHI::Format
  292. {
  293. for (int preferredIndex = 0; preferredIndex < preferredFormats.size(); ++preferredIndex)
  294. {
  295. for (int supportedIndex = 0; supportedIndex < supportedFormats.size(); ++supportedIndex)
  296. {
  297. if (supportedFormats[supportedIndex] == preferredFormats[preferredIndex])
  298. {
  299. return supportedFormats[supportedIndex];
  300. }
  301. }
  302. }
  303. // If no match found, just return the first supported format
  304. return supportedFormats[0];
  305. };
  306. const RHI::WindowHandle windowHandle = RHI::WindowHandle(reinterpret_cast<uintptr_t>(m_windowHandle));
  307. const AZStd::vector<RHI::Format> supportedFormats = device.GetValidSwapChainImageFormats(windowHandle);
  308. AZ_Assert(!supportedFormats.empty(), "There is no supported format for SwapChain images.");
  309. return GetPreferredFormat(preferredFormats, supportedFormats);
  310. }
  311. } // namespace RPI
  312. } // namespace AZ