From e8620894f7d0a933192eb769f9844c4f7e4845fb Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sun, 13 Aug 2023 18:16:03 +0200 Subject: [PATCH] Renderer: Expose present mode and allow to query/set it --- .../Nazara/OpenGLRenderer/OpenGLSwapchain.hpp | 6 + .../Nazara/OpenGLRenderer/Wrapper/Context.hpp | 4 +- .../Wrapper/EGL/EGLContextBase.hpp | 6 +- .../Wrapper/EGL/EGLFunctions.hpp | 2 + .../OpenGLRenderer/Wrapper/WGL/WGLContext.hpp | 4 +- .../OpenGLRenderer/Wrapper/WGL/WGLContext.inl | 2 +- .../OpenGLRenderer/Wrapper/Web/WebContext.hpp | 4 +- include/Nazara/Renderer/Enums.hpp | 18 ++ include/Nazara/Renderer/Swapchain.hpp | 5 + .../Nazara/Renderer/SwapchainParameters.hpp | 5 +- include/Nazara/VulkanRenderer/Utils.hpp | 2 + include/Nazara/VulkanRenderer/Utils.inl | 31 ++- .../Nazara/VulkanRenderer/VulkanSwapchain.hpp | 6 + src/Nazara/OpenGLRenderer/OpenGLSwapchain.cpp | 41 +++- src/Nazara/OpenGLRenderer/Wrapper/Context.cpp | 2 - .../Wrapper/EGL/EGLContextBase.cpp | 31 ++- .../OpenGLRenderer/Wrapper/WGL/WGLContext.cpp | 29 ++- .../OpenGLRenderer/Wrapper/Web/WebContext.cpp | 10 +- src/Nazara/VulkanRenderer/VulkanSwapchain.cpp | 64 ++++-- tests/PresentModeTest/main.cpp | 186 ++++++++++++++++++ tests/PresentModeTest/xmake.lua | 5 + 21 files changed, 420 insertions(+), 43 deletions(-) create mode 100644 tests/PresentModeTest/main.cpp create mode 100644 tests/PresentModeTest/xmake.lua diff --git a/include/Nazara/OpenGLRenderer/OpenGLSwapchain.hpp b/include/Nazara/OpenGLRenderer/OpenGLSwapchain.hpp index b43d2093e..2762cff68 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLSwapchain.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLSwapchain.hpp @@ -34,13 +34,17 @@ namespace Nz inline GL::Context& GetContext(); const OpenGLFramebuffer& GetFramebuffer(std::size_t i) const override; std::size_t GetFramebufferCount() const override; + PresentMode GetPresentMode() const override; const OpenGLRenderPass& GetRenderPass() const override; const Vector2ui& GetSize() const override; + PresentModeFlags GetSupportedPresentModes() const override; void NotifyResize(const Vector2ui& newSize) override; void Present(); + void SetPresentMode(PresentMode presentMode) override; + TransientResources& Transient() override; private: @@ -49,6 +53,8 @@ namespace Nz std::vector> m_renderImage; std::shared_ptr m_context; OpenGLWindowFramebuffer m_framebuffer; + PresentMode m_presentMode; + PresentModeFlags m_supportedPresentModes; Vector2ui m_size; bool m_sizeInvalidated; }; diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp index becf4c59c..f6a74f520 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp @@ -148,8 +148,6 @@ namespace Nz::GL inline bool DidLastCallSucceed() const; - virtual void EnableVerticalSync(bool enabled) = 0; - inline bool GetBoolean(GLenum name) const; inline bool GetBoolean(GLenum name, GLuint index) const; inline const OpenGLDevice* GetDevice() const; @@ -159,6 +157,7 @@ namespace Nz::GL template T GetInteger(GLenum name) const; template T GetInteger(GLenum name, GLuint index) const; inline const ContextParams& GetParams() const; + virtual PresentModeFlags GetSupportedPresentModes() const = 0; inline const OpenGLVaoCache& GetVaoCache() const; inline bool IsExtensionSupported(Extension extension) const; @@ -183,6 +182,7 @@ namespace Nz::GL inline void ResetStencilWriteMasks() const; void SetCurrentTextureUnit(UInt32 textureUnit) const; + virtual void SetPresentMode(PresentMode presentMode) = 0; void SetScissorBox(GLint x, GLint y, GLsizei width, GLsizei height) const; void SetViewport(GLint x, GLint y, GLsizei width, GLsizei height) const; diff --git a/include/Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.hpp b/include/Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.hpp index 31b08161b..54dd7a45a 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.hpp @@ -35,10 +35,12 @@ namespace Nz::GL virtual bool Create(const ContextParams& params, WindowHandle window, const EGLContextBase* shareContext = nullptr); virtual void Destroy(); - void EnableVerticalSync(bool enabled) override; + PresentModeFlags GetSupportedPresentModes() const override; inline bool HasPlatformExtension(const std::string& str) const; + void SetPresentMode(PresentMode presentMode) override; + void SwapBuffers() override; EGLContextBase& operator=(const EGLContextBase&) = delete; @@ -72,6 +74,8 @@ namespace Nz::GL std::unordered_set m_supportedPlatformExtensions; EGLContext m_handle; + EGLint m_maxSwapInterval; + EGLint m_minSwapInterval; bool m_ownsDisplay; }; } diff --git a/include/Nazara/OpenGLRenderer/Wrapper/EGL/EGLFunctions.hpp b/include/Nazara/OpenGLRenderer/Wrapper/EGL/EGLFunctions.hpp index a0ac53333..7bc782960 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/EGL/EGLFunctions.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/EGL/EGLFunctions.hpp @@ -17,6 +17,7 @@ NAZARA_OPENGLRENDERER_EGL_FUNC(eglCreatePbufferSurface, PFNEGLCREATEPBUFFERSURFA NAZARA_OPENGLRENDERER_EGL_FUNC(eglCreateWindowSurface, PFNEGLCREATEWINDOWSURFACEPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglDestroyContext, PFNEGLDESTROYCONTEXTPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglDestroySurface, PFNEGLDESTROYSURFACEPROC) +NAZARA_OPENGLRENDERER_EGL_FUNC(eglGetConfigAttrib, PFNEGLGETCONFIGATTRIBPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglGetDisplay, PFNEGLGETDISPLAYPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglGetError, PFNEGLGETERRORPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglGetProcAddress, PFNEGLGETPROCADDRESSPROC) @@ -24,6 +25,7 @@ NAZARA_OPENGLRENDERER_EGL_FUNC(eglInitialize, PFNEGLINITIALIZEPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglMakeCurrent, PFNEGLMAKECURRENTPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglQueryString, PFNEGLQUERYSTRINGPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglSwapBuffers, PFNEGLSWAPBUFFERSPROC) +NAZARA_OPENGLRENDERER_EGL_FUNC(eglSwapInterval, PFNEGLSWAPINTERVALPROC) NAZARA_OPENGLRENDERER_EGL_FUNC(eglTerminate, PFNEGLTERMINATEPROC) NAZARA_OPENGLRENDERER_EGL_FUNC_OPT(eglCreatePlatformWindowSurface, PFNEGLCREATEPLATFORMWINDOWSURFACEPROC) diff --git a/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.hpp b/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.hpp index f47fcc095..31b825402 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.hpp @@ -34,10 +34,12 @@ namespace Nz::GL bool Create(const WGLContext* baseContext, const ContextParams& params, WindowHandle window, const WGLContext* shareContext = nullptr); void Destroy(); - void EnableVerticalSync(bool enabled) override; + PresentModeFlags GetSupportedPresentModes() const override; inline bool HasPlatformExtension(const std::string& str) const; + void SetPresentMode(PresentMode presentMode) override; + void SwapBuffers() override; WGLContext& operator=(const WGLContext&) = delete; diff --git a/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.inl b/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.inl index d50cff786..74e1d44df 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.inl @@ -6,7 +6,7 @@ namespace Nz::GL { - inline GL::WGLContext::WGLContext(const OpenGLDevice* device, const WGLLoader& loader) : + inline WGLContext::WGLContext(const OpenGLDevice* device, const WGLLoader& loader) : Context(device), m_loader(loader), m_handle(nullptr) diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Web/WebContext.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Web/WebContext.hpp index 88d7482f6..9d4ace249 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Web/WebContext.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Web/WebContext.hpp @@ -33,10 +33,12 @@ namespace Nz::GL virtual bool Create(const ContextParams& params, WindowHandle window, const WebContext* shareContext = nullptr); virtual void Destroy(); - void EnableVerticalSync(bool enabled) override; + PresentModeFlags GetSupportedPresentModes() const override; inline bool HasPlatformExtension(const std::string& str) const; + void SetPresentMode(PresentMode presentMode) override; + void SwapBuffers() override; WebContext& operator=(const WebContext&) = delete; diff --git a/include/Nazara/Renderer/Enums.hpp b/include/Nazara/Renderer/Enums.hpp index da4deca40..0daa3a65e 100644 --- a/include/Nazara/Renderer/Enums.hpp +++ b/include/Nazara/Renderer/Enums.hpp @@ -109,6 +109,24 @@ namespace Nz using PipelineStageFlags = Flags; + enum class PresentMode + { + Immediate, + Mailbox, + RelaxedVerticalSync, + VerticalSync, + + Max = VerticalSync + }; + + template<> + struct EnumAsFlags + { + static constexpr PresentMode max = PresentMode::Max; + }; + + using PresentModeFlags = Flags; + enum class QueueType { Compute, diff --git a/include/Nazara/Renderer/Swapchain.hpp b/include/Nazara/Renderer/Swapchain.hpp index 9993b8edb..a519d4caa 100644 --- a/include/Nazara/Renderer/Swapchain.hpp +++ b/include/Nazara/Renderer/Swapchain.hpp @@ -32,8 +32,13 @@ namespace Nz virtual std::shared_ptr CreateCommandPool(QueueType queueType) = 0; + virtual PresentMode GetPresentMode() const = 0; + virtual PresentModeFlags GetSupportedPresentModes() const = 0; + virtual void NotifyResize(const Vector2ui& newSize) = 0; + virtual void SetPresentMode(PresentMode presentMode) = 0; + virtual TransientResources& Transient() = 0; protected: diff --git a/include/Nazara/Renderer/SwapchainParameters.hpp b/include/Nazara/Renderer/SwapchainParameters.hpp index 7d899d4eb..dbf2c83bd 100644 --- a/include/Nazara/Renderer/SwapchainParameters.hpp +++ b/include/Nazara/Renderer/SwapchainParameters.hpp @@ -8,6 +8,7 @@ #define NAZARA_RENDERER_SWAPCHAINPARAMETERS_HPP #include +#include #include #include @@ -15,8 +16,8 @@ namespace Nz { struct SwapchainParameters { - std::vector depthFormats = {Nz::PixelFormat::Depth24Stencil8, Nz::PixelFormat::Depth32FStencil8, Nz::PixelFormat::Depth16Stencil8, Nz::PixelFormat::Depth32F, Nz::PixelFormat::Depth24}; //< By order of preference - bool verticalSync = false; + std::vector depthFormats = { Nz::PixelFormat::Depth24Stencil8, Nz::PixelFormat::Depth32FStencil8, Nz::PixelFormat::Depth16Stencil8, Nz::PixelFormat::Depth32F, Nz::PixelFormat::Depth24 }; //< By order of preference + std::vector presentMode = { PresentMode::Mailbox, PresentMode::Immediate, PresentMode::RelaxedVerticalSync, PresentMode::VerticalSync }; //< By order of preference }; } diff --git a/include/Nazara/VulkanRenderer/Utils.hpp b/include/Nazara/VulkanRenderer/Utils.hpp index 4709415be..c62314f7b 100644 --- a/include/Nazara/VulkanRenderer/Utils.hpp +++ b/include/Nazara/VulkanRenderer/Utils.hpp @@ -18,6 +18,7 @@ namespace Nz { inline std::optional FromVulkan(VkFormat format); + inline std::optional FromVulkan(VkPresentModeKHR presentMode); inline VkAttachmentLoadOp ToVulkan(AttachmentLoadOp loadOp); inline VkAttachmentStoreOp ToVulkan(AttachmentStoreOp storeOp); @@ -33,6 +34,7 @@ namespace Nz inline VkPipelineStageFlags ToVulkan(PipelineStageFlags pipelineStages); inline VkFormat ToVulkan(PixelFormat pixelFormat); inline VkImageAspectFlags ToVulkan(PixelFormatContent pixelFormatContent); + inline VkPresentModeKHR ToVulkan(PresentMode presentMode); inline VkPrimitiveTopology ToVulkan(PrimitiveMode primitiveMode); inline VkCompareOp ToVulkan(RendererComparison comparison); inline VkFilter ToVulkan(SamplerFilter samplerFilter); diff --git a/include/Nazara/VulkanRenderer/Utils.inl b/include/Nazara/VulkanRenderer/Utils.inl index 91885c1d6..7b814dece 100644 --- a/include/Nazara/VulkanRenderer/Utils.inl +++ b/include/Nazara/VulkanRenderer/Utils.inl @@ -32,6 +32,20 @@ namespace Nz return std::nullopt; } + std::optional FromVulkan(VkPresentModeKHR presentMode) + { + switch (presentMode) + { + case VK_PRESENT_MODE_IMMEDIATE_KHR: return PresentMode::Immediate; + case VK_PRESENT_MODE_MAILBOX_KHR: return PresentMode::Mailbox; + case VK_PRESENT_MODE_FIFO_KHR: return PresentMode::VerticalSync; + case VK_PRESENT_MODE_FIFO_RELAXED_KHR: return PresentMode::RelaxedVerticalSync; + default: break; + } + + return std::nullopt; + } + inline VkAttachmentLoadOp ToVulkan(AttachmentLoadOp loadOp) { switch (loadOp) @@ -306,6 +320,20 @@ namespace Nz return VK_IMAGE_ASPECT_COLOR_BIT; } + VkPresentModeKHR ToVulkan(PresentMode presentMode) + { + switch (presentMode) + { + case PresentMode::Immediate: return VK_PRESENT_MODE_IMMEDIATE_KHR; + case PresentMode::Mailbox: return VK_PRESENT_MODE_MAILBOX_KHR; + case PresentMode::RelaxedVerticalSync: return VK_PRESENT_MODE_FIFO_RELAXED_KHR; + case PresentMode::VerticalSync: return VK_PRESENT_MODE_FIFO_KHR; + } + + NazaraError("Unhandled PresentMode 0x" + NumberToString(UnderlyingCast(presentMode), 16)); + return VK_PRESENT_MODE_FIFO_KHR; + } + inline VkPrimitiveTopology ToVulkan(PrimitiveMode primitiveMode) { switch (primitiveMode) @@ -318,7 +346,7 @@ namespace Nz case PrimitiveMode::TriangleFan: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; } - NazaraError("Unhandled FaceFilling 0x" + NumberToString(UnderlyingCast(primitiveMode), 16)); + NazaraError("Unhandled PrimitiveMode 0x" + NumberToString(UnderlyingCast(primitiveMode), 16)); return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; } @@ -502,3 +530,4 @@ namespace Nz } #include +#include "Utils.hpp" diff --git a/include/Nazara/VulkanRenderer/VulkanSwapchain.hpp b/include/Nazara/VulkanRenderer/VulkanSwapchain.hpp index aa184b37c..0756e5702 100644 --- a/include/Nazara/VulkanRenderer/VulkanSwapchain.hpp +++ b/include/Nazara/VulkanRenderer/VulkanSwapchain.hpp @@ -53,12 +53,16 @@ namespace Nz inline Vk::QueueHandle& GetGraphicsQueue(); const VulkanRenderPass& GetRenderPass() const override; const Vector2ui& GetSize() const override; + PresentMode GetPresentMode() const override; + PresentModeFlags GetSupportedPresentModes() const override; inline const Vk::Swapchain& GetSwapchain() const; void NotifyResize(const Vector2ui& newSize) override; void Present(UInt32 imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE); + void SetPresentMode(PresentMode presentMode) override; + TransientResources& Transient() override; VulkanSwapchain& operator=(const VulkanSwapchain&) = delete; @@ -85,6 +89,8 @@ namespace Nz Vk::QueueHandle m_transferQueue; Vk::Surface m_surface; Vk::Swapchain m_swapchain; + PresentMode m_presentMode; + PresentModeFlags m_supportedPresentModes; Vector2ui m_swapchainSize; VkFormat m_depthStencilFormat; VkSurfaceFormatKHR m_surfaceFormat; diff --git a/src/Nazara/OpenGLRenderer/OpenGLSwapchain.cpp b/src/Nazara/OpenGLRenderer/OpenGLSwapchain.cpp index d18659761..56551ab9e 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLSwapchain.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLSwapchain.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include namespace Nz @@ -54,6 +53,23 @@ namespace Nz else depthFormat = PixelFormat::Undefined; + m_supportedPresentModes = m_context->GetSupportedPresentModes(); + +#ifdef NAZARA_PLATFORM_WEB + m_presentMode = PresentMode::Immediate; //< default present mode +#else + m_presentMode = PresentMode::VerticalSync; //< default present mode +#endif + + for (PresentMode presentMode : parameters.presentMode) + { + if (m_supportedPresentModes & presentMode) + { + SetPresentMode(presentMode); + break; + } + } + std::vector attachments; std::vector subpassDescriptions; std::vector subpassDependencies; @@ -78,7 +94,7 @@ namespace Nz std::shared_ptr OpenGLSwapchain::CreateCommandPool(QueueType /*queueType*/) { - return std::make_unique(); + return std::make_shared(); } const OpenGLFramebuffer& OpenGLSwapchain::GetFramebuffer(std::size_t i) const @@ -98,11 +114,21 @@ namespace Nz return *m_renderPass; } + PresentMode OpenGLSwapchain::GetPresentMode() const + { + return m_presentMode; + } + const Vector2ui& OpenGLSwapchain::GetSize() const { return m_size; } + PresentModeFlags OpenGLSwapchain::GetSupportedPresentModes() const + { + return m_supportedPresentModes; + } + void OpenGLSwapchain::NotifyResize(const Vector2ui& newSize) { OnRenderTargetSizeChange(this, newSize); @@ -117,6 +143,17 @@ namespace Nz m_currentFrame = (m_currentFrame + 1) % m_renderImage.size(); } + void OpenGLSwapchain::SetPresentMode(PresentMode presentMode) + { + NazaraAssert(m_supportedPresentModes & presentMode, "unsupported present mode"); + + if (m_presentMode != presentMode) + { + m_context->SetPresentMode(presentMode); + m_presentMode = presentMode; + } + } + TransientResources& OpenGLSwapchain::Transient() { return *m_renderImage[m_currentFrame]; diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index 82064b6fb..c30c3e29a 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -670,8 +670,6 @@ namespace Nz::GL m_state.renderStates.scissorTest = false; m_state.renderStates.stencilTest = false; - EnableVerticalSync(false); - return true; } diff --git a/src/Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.cpp b/src/Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.cpp index 1ef42da96..daaf42a36 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/EGL/EGLContextBase.cpp @@ -87,9 +87,33 @@ namespace Nz::GL } } - void EGLContextBase::EnableVerticalSync(bool enabled) + PresentModeFlags EGLContextBase::GetSupportedPresentModes() const { - // TODO + PresentModeFlags supportedModes; + if (m_maxSwapInterval >= 1) + supportedModes |= PresentMode::VerticalSync; + + if (m_minSwapInterval <= 0) + supportedModes |= PresentMode::Immediate; + + if (m_minSwapInterval <= -1) + supportedModes |= PresentMode::RelaxedVerticalSync; + + return supportedModes; + } + + void EGLContextBase::SetPresentMode(PresentMode presentMode) + { + int interval = 0; + switch (presentMode) + { + case PresentMode::Immediate: interval = 0; break; + case PresentMode::RelaxedVerticalSync: interval = -1; break; + case PresentMode::VerticalSync: interval = 1; break; + default: return; // TODO: Unreachable + } + + m_loader.eglSwapInterval(m_display, interval); } void EGLContextBase::SwapBuffers() @@ -247,6 +271,9 @@ namespace Nz::GL return false; } + m_loader.eglGetConfigAttrib(m_display, config, EGL_MAX_SWAP_INTERVAL, &m_maxSwapInterval); + m_loader.eglGetConfigAttrib(m_display, config, EGL_MIN_SWAP_INTERVAL, &m_minSwapInterval); + LoadEGLExt(); return true; diff --git a/src/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.cpp b/src/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.cpp index 9eeedadc8..b563e1461 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/WGL/WGLContext.cpp @@ -73,15 +73,34 @@ namespace Nz::GL } } - void WGLContext::EnableVerticalSync(bool enabled) + PresentModeFlags WGLContext::GetSupportedPresentModes() const { + PresentModeFlags supportedModes = PresentMode::Immediate; if (wglSwapIntervalEXT) { - if (!SetCurrentContext(this)) - return; - - wglSwapIntervalEXT(enabled); + supportedModes |= PresentMode::VerticalSync; + if (HasPlatformExtension("WGL_EXT_swap_control_tear")) + supportedModes |= PresentMode::RelaxedVerticalSync; } + + return supportedModes; + } + + void WGLContext::SetPresentMode(PresentMode presentMode) + { + if (!SetCurrentContext(this)) + return; + + int interval = 0; + switch (presentMode) + { + case PresentMode::Immediate: interval = 0; break; + case PresentMode::RelaxedVerticalSync: interval = -1; break; //< WGL_EXT_swap_control_tear + case PresentMode::VerticalSync: interval = 1; break; + default: return; // TODO: Unreachable + } + + wglSwapIntervalEXT(interval); } void WGLContext::SwapBuffers() diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Web/WebContext.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Web/WebContext.cpp index fd7947749..3bb493221 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Web/WebContext.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Web/WebContext.cpp @@ -64,9 +64,15 @@ namespace Nz::GL } } - void WebContext::EnableVerticalSync(bool /*enabled*/) + PresentModeFlags WebContext::GetSupportedPresentModes() const { - // TODO + // WebGL does not support disabling V-Sync + return PresentMode::VerticalSync; + } + + void WebContext::SetPresentMode(PresentMode /*presentMode*/) + { + // Nothing to do } void WebContext::SwapBuffers() diff --git a/src/Nazara/VulkanRenderer/VulkanSwapchain.cpp b/src/Nazara/VulkanRenderer/VulkanSwapchain.cpp index a1ef3c910..7ee00aa30 100644 --- a/src/Nazara/VulkanRenderer/VulkanSwapchain.cpp +++ b/src/Nazara/VulkanRenderer/VulkanSwapchain.cpp @@ -151,6 +151,27 @@ namespace Nz throw std::runtime_error("failed to find a support depth-stencil format"); } + std::vector presentModes; + if (!m_surface.GetPresentModes(physDeviceInfo.physDevice, &presentModes)) + throw std::runtime_error("failed to query supported present modes"); + + m_supportedPresentModes.Clear(); + for (VkPresentModeKHR vkPresentMode : presentModes) + { + if (auto presentModeOpt = FromVulkan(vkPresentMode)) + m_supportedPresentModes |= *presentModeOpt; + } + + m_presentMode = PresentMode::VerticalSync; //< guaranteed to be supported + for (PresentMode presentMode : parameters.presentMode) + { + if (m_supportedPresentModes & presentMode) + { + m_presentMode = presentMode; + break; + } + } + if (!SetupRenderPass()) throw std::runtime_error("failed to create renderpass"); @@ -292,6 +313,16 @@ namespace Nz return m_swapchainSize; } + PresentMode VulkanSwapchain::GetPresentMode() const + { + return m_presentMode; + } + + PresentModeFlags VulkanSwapchain::GetSupportedPresentModes() const + { + return m_supportedPresentModes; + } + void VulkanSwapchain::NotifyResize(const Vector2ui& newSize) { OnRenderTargetSizeChange(this, newSize); @@ -332,6 +363,17 @@ namespace Nz } } + void VulkanSwapchain::SetPresentMode(PresentMode presentMode) + { + NazaraAssert(m_supportedPresentModes & presentMode, "unsupported present mode"); + + if (m_presentMode != presentMode) + { + m_presentMode = presentMode; + m_shouldRecreateSwapchain = true; + } + } + TransientResources& VulkanSwapchain::Transient() { return *m_concurrentImageData[m_currentFrame]; @@ -566,26 +608,6 @@ namespace Nz else extent = surfaceCapabilities.currentExtent; - std::vector presentModes; - if (!m_surface.GetPresentModes(deviceInfo.physDevice, &presentModes)) - { - NazaraError("Failed to query supported present modes"); - return false; - } - - VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; - for (VkPresentModeKHR presentMode : presentModes) - { - if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) - { - swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; - break; - } - - if (presentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) - swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; - } - VkCompositeAlphaFlagBitsKHR compositeAlpha; if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; @@ -610,7 +632,7 @@ namespace Nz 0, nullptr, surfaceCapabilities.currentTransform, compositeAlpha, - swapchainPresentMode, + ToVulkan(m_presentMode), VK_TRUE, m_swapchain }; diff --git a/tests/PresentModeTest/main.cpp b/tests/PresentModeTest/main.cpp new file mode 100644 index 000000000..ead4e7aa8 --- /dev/null +++ b/tests/PresentModeTest/main.cpp @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + Nz::Renderer::Config rendererConfig; +#ifndef NAZARA_PLATFORM_WEB + std::cout << "Run using Vulkan? (y/n)" << std::endl; + if (std::getchar() == 'y') + rendererConfig.preferredAPI = Nz::RenderAPI::Vulkan; + else + rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL; +#endif + + Nz::Application app(rendererConfig); + + auto& windowing = app.AddComponent(); + + auto& ecs = app.AddComponent(); + + auto& world = ecs.AddWorld(); + Nz::RenderSystem& renderSystem = world.AddSystem(); + + std::string windowTitle = "Physics 2D"; + Nz::Window& window = windowing.CreateWindow(Nz::VideoMode(1920, 1080, 32), windowTitle); + Nz::WindowSwapchain& windowSwapchain = renderSystem.CreateSwapchain(window); + Nz::Swapchain& swapchain = windowSwapchain.GetSwapchain(); + + Nz::Vector2ui windowSize = window.GetSize(); + + entt::handle viewer = world.CreateEntity(); + { + viewer.emplace(); + viewer.emplace(&windowSwapchain, Nz::ProjectionType::Orthographic); + } + + // Turn present mode flags into vector for easier processing + Nz::FixedVector> supportedPresentModes; + for (Nz::PresentMode presentMode : swapchain.GetSupportedPresentModes()) + supportedPresentModes.push_back(presentMode); + + auto presentFlagIt = std::find(supportedPresentModes.begin(), supportedPresentModes.end(), swapchain.GetPresentMode()); + + bool limitFps = false; + + std::shared_ptr presentModeText = std::make_shared(); + auto UpdatePresentModeText = [&] + { + Nz::RichTextDrawer textDrawer; + textDrawer.SetDefaultStyle(Nz::TextStyle::Bold); + textDrawer.AppendText("Supported present modes:\n"); + textDrawer.SetDefaultStyle(Nz::TextStyle_Regular); + textDrawer.AppendText("Use +/- to switch present mode (and * to limit FPS to 50)\n"); + + for (Nz::PresentMode presentMode : supportedPresentModes) + { + textDrawer.AppendText("- "); + + if (presentMode == swapchain.GetPresentMode()) + textDrawer.SetDefaultColor(Nz::Color::Yellow()); + else + textDrawer.SetDefaultColor(Nz::Color::White()); + + switch (presentMode) + { + case Nz::PresentMode::Immediate: textDrawer.AppendText("Immediate\n"); break; + case Nz::PresentMode::Mailbox: textDrawer.AppendText("Mailbox\n"); break; + case Nz::PresentMode::RelaxedVerticalSync: textDrawer.AppendText("RelaxedVerticalSync\n"); break; + case Nz::PresentMode::VerticalSync: textDrawer.AppendText("VerticalSync\n"); break; + } + } + + textDrawer.SetDefaultColor(Nz::Color::White()); + textDrawer.AppendText("Use * to limit FPS to 50\n"); + if (limitFps) + { + textDrawer.SetDefaultColor(Nz::Color::Red()); + textDrawer.AppendText("FPS limited to 50\n"); + } + else + textDrawer.AppendText("Unlimited FPS\n"); + + presentModeText->Update(textDrawer); + }; + + UpdatePresentModeText(); + + entt::handle textEntity = world.CreateEntity(); + { + textEntity.emplace(); + textEntity.emplace(presentModeText); + } + + entt::handle spriteEntity = world.CreateEntity(); + { + std::shared_ptr sprite = std::make_shared(Nz::MaterialInstance::GetDefault(Nz::MaterialType::Basic)); + sprite->SetSize({ 128.f, 128.f }); + + spriteEntity.emplace(); + spriteEntity.emplace(sprite); + } + + Nz::WindowEventHandler& eventHandler = window.GetEventHandler(); + eventHandler.OnKeyPressed.Connect([&](const Nz::WindowEventHandler*, const Nz::WindowEvent::KeyEvent& event) + { + if (event.virtualKey == Nz::Keyboard::VKey::Add) + { + ++presentFlagIt; + if (presentFlagIt == supportedPresentModes.end()) + presentFlagIt = supportedPresentModes.begin(); + + swapchain.SetPresentMode(*presentFlagIt); + UpdatePresentModeText(); + } + else if (event.virtualKey == Nz::Keyboard::VKey::Subtract) + { + if (presentFlagIt == supportedPresentModes.begin()) + presentFlagIt = supportedPresentModes.end(); + + --presentFlagIt; + + swapchain.SetPresentMode(*presentFlagIt); + UpdatePresentModeText(); + } + else if (event.virtualKey == Nz::Keyboard::VKey::Multiply) + { + limitFps = !limitFps; + UpdatePresentModeText(); + } + }); + + Nz::Time accumulatorTime = Nz::Time::Zero(); + constexpr Nz::Time timeToMove = Nz::Time::Second(); + + Nz::HighPrecisionClock fpsLimitClock; + Nz::MillisecondClock fpsClock; + unsigned int fps = 0; + bool forward = true; + app.AddUpdaterFunc([&](Nz::Time elapsedTime) + { + // Move sprite + if (forward) + { + accumulatorTime += elapsedTime; + if (accumulatorTime >= timeToMove) + forward = false; + } + else + { + accumulatorTime -= elapsedTime; + if (accumulatorTime <= Nz::Time::Zero()) + forward = true; + } + + float delta = (accumulatorTime.AsSeconds() / timeToMove.AsSeconds()); + + spriteEntity.get().SetPosition(Nz::Lerp(0.f, windowSize.x - 128.f, delta * delta * (3.f - 2.f * delta)), windowSize.y / 2.f - 128.f / 2.f, 0.f); + + // Limit FPS + if (limitFps) + { + Nz::Time remainingTime = Nz::Time::TickDuration(50) - fpsLimitClock.GetElapsedTime(); + if (remainingTime > Nz::Time::Zero()) + std::this_thread::sleep_for(remainingTime.AsDuration()); + + fpsLimitClock.Restart(); + } + + // FPS update + fps++; + + if (fpsClock.RestartIfOver(Nz::Time::Second())) + { + window.SetTitle(windowTitle + " - " + Nz::NumberToString(fps) + " FPS"); + fps = 0; + } + }); + + return app.Run(); +} diff --git a/tests/PresentModeTest/xmake.lua b/tests/PresentModeTest/xmake.lua new file mode 100644 index 000000000..e0c7af263 --- /dev/null +++ b/tests/PresentModeTest/xmake.lua @@ -0,0 +1,5 @@ +target("PresentModeTest") + add_deps("NazaraGraphics") + add_packages("entt") + add_defines("NAZARA_ENTT") + add_files("main.cpp")