From 2232eb6eb37b08097ef147d68ae2baf9f120668e Mon Sep 17 00:00:00 2001 From: Laura Hermanns Date: Wed, 7 Aug 2024 23:38:57 -0400 Subject: [PATCH] [SwapChain] Added IsPresentable() to SwapChain interface. This new function allows to detect when the native surface of a swap-chain is temporarily unavailable. This occurs when a mobile app is paused and the native surface is destroyed, while it is later re-initialized when the app resumes. Also moved all SwapChain interface declarations to Backend/SwapChain.inl header as for other interfaces with more than one or two functions. --- examples/Cpp/ExampleBase/ExampleBase.cpp | 42 +++++++++++------ include/LLGL-C/SwapChain.h | 1 + include/LLGL/Backend/SwapChain.inl | 46 +++++++++++++++++++ include/LLGL/SwapChain.h | 22 +++++++++ sources/Platform/Android/AndroidCanvas.cpp | 6 --- .../Renderer/DebugLayer/DbgCommandBuffer.cpp | 8 ++++ sources/Renderer/DebugLayer/DbgSwapChain.cpp | 5 ++ sources/Renderer/DebugLayer/DbgSwapChain.h | 15 ++---- .../Renderer/Direct3D11/D3D11SwapChain.cpp | 5 ++ sources/Renderer/Direct3D11/D3D11SwapChain.h | 17 ++----- .../Renderer/Direct3D12/D3D12SwapChain.cpp | 5 ++ sources/Renderer/Direct3D12/D3D12SwapChain.h | 15 ++---- sources/Renderer/Metal/MTSwapChain.h | 17 ++----- sources/Renderer/Metal/MTSwapChain.mm | 5 ++ sources/Renderer/Null/NullSwapChain.cpp | 5 ++ sources/Renderer/Null/NullSwapChain.h | 23 +++------- sources/Renderer/OpenGL/GLSwapChain.cpp | 23 +++++++++- sources/Renderer/OpenGL/GLSwapChain.h | 21 ++------- .../Android/AndroidGLSwapChainContext.cpp | 5 ++ .../Android/AndroidGLSwapChainContext.h | 1 + .../OpenGL/Platform/GLSwapChainContext.h | 3 ++ .../Platform/IOS/IOSGLSwapChainContext.h | 2 +- .../Platform/IOS/IOSGLSwapChainContext.mm | 5 ++ .../Linux/LinuxGLSwapChainContext.cpp | 8 +++- .../Platform/Linux/LinuxGLSwapChainContext.h | 1 + .../Platform/MacOS/MacOSGLSwapChainContext.h | 2 +- .../Platform/MacOS/MacOSGLSwapChainContext.mm | 5 ++ .../Win32/Win32GLSwapChainContext.cpp | 8 +++- .../Platform/Win32/Win32GLSwapChainContext.h | 5 +- sources/Renderer/Vulkan/VKSwapChain.cpp | 6 +++ sources/Renderer/Vulkan/VKSwapChain.h | 19 ++------ wrapper/C99/C99SwapChain.cpp | 5 ++ wrapper/CSharp/LLGLWrapper.cs | 4 ++ wrapper/CSharp/SwapChain.cs | 8 ++++ 34 files changed, 241 insertions(+), 127 deletions(-) create mode 100644 include/LLGL/Backend/SwapChain.inl diff --git a/examples/Cpp/ExampleBase/ExampleBase.cpp b/examples/Cpp/ExampleBase/ExampleBase.cpp index 21fe499af9..87010b5bbd 100644 --- a/examples/Cpp/ExampleBase/ExampleBase.cpp +++ b/examples/Cpp/ExampleBase/ExampleBase.cpp @@ -11,6 +11,7 @@ #include "ImageReader.h" #include "FileUtils.h" #include +#include #define STB_IMAGE_IMPLEMENTATION #include @@ -380,13 +381,25 @@ void ExampleBase::Run() bool fullscreen = false; const LLGL::Extent2D initialResolution = swapChain->GetResolution(); - #ifdef LLGL_MOBILE_PLATFORM - while (LLGL::Surface::ProcessEvents()) - #else + #ifndef LLGL_MOBILE_PLATFORM LLGL::Window& window = LLGL::CastTo(swapChain->GetSurface()); - while (LLGL::Surface::ProcessEvents() && !window.HasQuit() && !input.KeyDown(LLGL::Key::Escape)) #endif + + while (LLGL::Surface::ProcessEvents() && !input.KeyDown(LLGL::Key::Escape)) { + #ifndef LLGL_MOBILE_PLATFORM + // On desktop platforms, we also want to quit the app if the close button has been pressed + if (window.HasQuit()) + break; + #endif + + // On mobile platforms, if app has paused, the swap-chain might not be presentable until the app is resumed again + if (!swapChain->IsPresentable()) + { + std::this_thread::yield(); + continue; + } + #ifdef LLGL_OS_ANDROID if (input.KeyDown(LLGL::Key::BrowserBack)) ANativeActivity_finish(ExampleBase::androidApp_->activity); @@ -484,17 +497,20 @@ ExampleBase::ExampleBase(const LLGL::UTF8String& title) LLGL::RenderSystemDescriptor rendererDesc = g_Config.rendererModule; #if defined LLGL_OS_ANDROID - LLGL::RendererConfigurationOpenGL cfgGL; - if (android_app* app = ExampleBase::androidApp_) + if (rendererDesc.moduleName == "OpenGLES3") { - rendererDesc.androidApp = app; - cfgGL.majorVersion = 3; - cfgGL.minorVersion = 1; - rendererDesc.rendererConfig = &cfgGL; - rendererDesc.rendererConfigSize = sizeof(cfgGL); + LLGL::RendererConfigurationOpenGL cfgGL; + if (android_app* app = ExampleBase::androidApp_) + { + rendererDesc.androidApp = app; + cfgGL.majorVersion = 3; + cfgGL.minorVersion = 1; + rendererDesc.rendererConfig = &cfgGL; + rendererDesc.rendererConfigSize = sizeof(cfgGL); + } + else + throw std::invalid_argument("'android_app' state was not specified"); } - else - throw std::invalid_argument("'android_app' state was not specified"); #endif if (g_Config.debugger) diff --git a/include/LLGL-C/SwapChain.h b/include/LLGL-C/SwapChain.h index a514d4f5be..8fb2ff44ad 100644 --- a/include/LLGL-C/SwapChain.h +++ b/include/LLGL-C/SwapChain.h @@ -14,6 +14,7 @@ #include +LLGL_C_EXPORT bool llglIsPresentable(LLGLSwapChain swapChain); LLGL_C_EXPORT void llglPresent(LLGLSwapChain swapChain); LLGL_C_EXPORT uint32_t llglGetCurrentSwapIndex(LLGLSwapChain swapChain); LLGL_C_EXPORT uint32_t llglGetNumSwapBuffers(LLGLSwapChain swapChain); diff --git a/include/LLGL/Backend/SwapChain.inl b/include/LLGL/Backend/SwapChain.inl new file mode 100644 index 0000000000..d845a177c7 --- /dev/null +++ b/include/LLGL/Backend/SwapChain.inl @@ -0,0 +1,46 @@ +/* + * SwapChain.inl + * + * Copyright (c) 2015 Lukas Hermanns. All rights reserved. + * Licensed under the terms of the BSD 3-Clause license (see LICENSE.txt). + */ + +virtual bool IsPresentable( + void +) const override final; + +virtual void Present( + void +) override final; + +virtual std::uint32_t GetCurrentSwapIndex( + void +) const override final; + +virtual std::uint32_t GetNumSwapBuffers( + void +) const override final; + +virtual std::uint32_t GetSamples( + void +) const override final; + +virtual LLGL::Format GetColorFormat( + void +) const override final; + +virtual LLGL::Format GetDepthStencilFormat( + void +) const override final; + +virtual const LLGL::RenderPass* GetRenderPass( + void +) const override; + +virtual bool SetVsyncInterval( + std::uint32_t vsyncInterval +) override final; + + + +// ================================================================================ diff --git a/include/LLGL/SwapChain.h b/include/LLGL/SwapChain.h index 2100197478..269ae2b00d 100644 --- a/include/LLGL/SwapChain.h +++ b/include/LLGL/SwapChain.h @@ -76,6 +76,28 @@ class LLGL_EXPORT SwapChain : public RenderTarget /* ----- Back Buffer ----- */ + /** + \brief Returns true if this swap-chain is ready for rendering and presenting. Otherwise, the native surface or back buffer are not available. + \remarks This is mostly used for mobile platforms where the surface can be temporarily destroyed when the app is paused and later re-initialized when the app resumes. + If this is false, no rendering to this swap-chain must be performed nor can the back buffer be presented. + \remarks A safe way to handle the event of a lost native surface is to skip the entire rendering when this returns false as shown below: + \code + while (LLGL::Surface::ProcessEvents()) { + // Skip frame if swap-chain is currently not presentable + if (!swapChain->IsPresentable()) { + std::this_thread::yield(); + continue; + } + + // Render frame ... + + // Present swap-chain back buffer + swapChain->Present(); + } + \endcode + */ + virtual bool IsPresentable() const = 0; + /** \brief Swaps the current back buffer with the front buffer to present it on the screen. \see GetCurrentSwapIndex diff --git a/sources/Platform/Android/AndroidCanvas.cpp b/sources/Platform/Android/AndroidCanvas.cpp index 6d8832a739..1dc5e566cc 100644 --- a/sources/Platform/Android/AndroidCanvas.cpp +++ b/sources/Platform/Android/AndroidCanvas.cpp @@ -34,12 +34,6 @@ bool Surface::ProcessEvents() if (source != nullptr) source->process(app, source); - /* Process sensor data */ - /*if (ident == LOOPER_ID_USER) - { - //TODO - }*/ - /* Check if we are exiting */ if (app->destroyRequested != 0) return false; diff --git a/sources/Renderer/DebugLayer/DbgCommandBuffer.cpp b/sources/Renderer/DebugLayer/DbgCommandBuffer.cpp index b1446faf96..5f5cefcd27 100644 --- a/sources/Renderer/DebugLayer/DbgCommandBuffer.cpp +++ b/sources/Renderer/DebugLayer/DbgCommandBuffer.cpp @@ -743,6 +743,14 @@ void DbgCommandBuffer::BeginRenderPass( { auto& swapChainDbg = LLGL_DBG_CAST(DbgSwapChain&, renderTarget); + if (!swapChainDbg.IsPresentable()) + { + LLGL_DBG_ERROR( + ErrorType::InvalidState, + "cannot begin new render pass with swap chain that is not presentable" + ); + } + swapChainDbg.NotifyNextRenderPass(debugger_, renderPass); bindings_.swapChain = &swapChainDbg; diff --git a/sources/Renderer/DebugLayer/DbgSwapChain.cpp b/sources/Renderer/DebugLayer/DbgSwapChain.cpp index 3f95a2fb5e..c853462c59 100644 --- a/sources/Renderer/DebugLayer/DbgSwapChain.cpp +++ b/sources/Renderer/DebugLayer/DbgSwapChain.cpp @@ -52,6 +52,11 @@ void DbgSwapChain::SetDebugName(const char* name) DbgSetObjectName(*this, name); } +bool DbgSwapChain::IsPresentable() const +{ + return instance.IsPresentable(); +} + void DbgSwapChain::Present() { instance.Present(); diff --git a/sources/Renderer/DebugLayer/DbgSwapChain.h b/sources/Renderer/DebugLayer/DbgSwapChain.h index 91595077a2..234a51ded1 100644 --- a/sources/Renderer/DebugLayer/DbgSwapChain.h +++ b/sources/Renderer/DebugLayer/DbgSwapChain.h @@ -32,20 +32,11 @@ class DbgSwapChain final : public SwapChain public: - void SetDebugName(const char* name) override; - - void Present() override; - - std::uint32_t GetCurrentSwapIndex() const override; - std::uint32_t GetNumSwapBuffers() const override; - std::uint32_t GetSamples() const override; + #include - Format GetColorFormat() const override; - Format GetDepthStencilFormat() const override; - - bool SetVsyncInterval(std::uint32_t vsyncInterval) override; + public: - const RenderPass* GetRenderPass() const override; + void SetDebugName(const char* name) override; public: diff --git a/sources/Renderer/Direct3D11/D3D11SwapChain.cpp b/sources/Renderer/Direct3D11/D3D11SwapChain.cpp index 28f4fef172..7936ef89bf 100644 --- a/sources/Renderer/Direct3D11/D3D11SwapChain.cpp +++ b/sources/Renderer/Direct3D11/D3D11SwapChain.cpp @@ -79,6 +79,11 @@ void D3D11SwapChain::SetDebugName(const char* name) } } +bool D3D11SwapChain::IsPresentable() const +{ + return true; // dummy +} + void D3D11SwapChain::Present() { const bool tearingEnabled = (tearingSupported_ && windowedMode_ && swapChainInterval_ == 0); diff --git a/sources/Renderer/Direct3D11/D3D11SwapChain.h b/sources/Renderer/Direct3D11/D3D11SwapChain.h index 6f110269be..7e0c88e27d 100644 --- a/sources/Renderer/Direct3D11/D3D11SwapChain.h +++ b/sources/Renderer/Direct3D11/D3D11SwapChain.h @@ -29,6 +29,10 @@ class D3D11RenderSystem; class D3D11SwapChain final : public SwapChain { + public: + + #include + public: D3D11SwapChain( @@ -41,19 +45,6 @@ class D3D11SwapChain final : public SwapChain void SetDebugName(const char* name) override; - void Present() override; - - std::uint32_t GetCurrentSwapIndex() const override; - std::uint32_t GetNumSwapBuffers() const override; - std::uint32_t GetSamples() const override; - - Format GetColorFormat() const override; - Format GetDepthStencilFormat() const override; - - const RenderPass* GetRenderPass() const override; - - bool SetVsyncInterval(std::uint32_t vsyncInterval) override; - public: // Copyies a subresource region from the backbuffer (color or depth-stencil) into the destination resource. diff --git a/sources/Renderer/Direct3D12/D3D12SwapChain.cpp b/sources/Renderer/Direct3D12/D3D12SwapChain.cpp index cbfbe9b5d4..d23014df2d 100644 --- a/sources/Renderer/Direct3D12/D3D12SwapChain.cpp +++ b/sources/Renderer/Direct3D12/D3D12SwapChain.cpp @@ -108,6 +108,11 @@ void D3D12SwapChain::SetDebugName(const char* name) } } +bool D3D12SwapChain::IsPresentable() const +{ + return true; // dummy +} + void D3D12SwapChain::Present() { /* Present swap-chain with vsync interval */ diff --git a/sources/Renderer/Direct3D12/D3D12SwapChain.h b/sources/Renderer/Direct3D12/D3D12SwapChain.h index 005ee33974..3b66d913aa 100644 --- a/sources/Renderer/Direct3D12/D3D12SwapChain.h +++ b/sources/Renderer/Direct3D12/D3D12SwapChain.h @@ -36,20 +36,11 @@ class D3D12SwapChain final : public SwapChain public: - void SetDebugName(const char* name) override; - - void Present() override; - - std::uint32_t GetCurrentSwapIndex() const override; - std::uint32_t GetNumSwapBuffers() const override; - std::uint32_t GetSamples() const override; + #include - Format GetColorFormat() const override; - Format GetDepthStencilFormat() const override; - - const RenderPass* GetRenderPass() const override; + public: - bool SetVsyncInterval(std::uint32_t vsyncInterval) override; + void SetDebugName(const char* name) override; public: diff --git a/sources/Renderer/Metal/MTSwapChain.h b/sources/Renderer/Metal/MTSwapChain.h index e23b594a6f..5476b23e0a 100644 --- a/sources/Renderer/Metal/MTSwapChain.h +++ b/sources/Renderer/Metal/MTSwapChain.h @@ -31,7 +31,9 @@ class MTSwapChain final : public SwapChain public: - /* ----- Common ----- */ + #include + + public: MTSwapChain( id device, @@ -40,19 +42,6 @@ class MTSwapChain final : public SwapChain const RendererInfo& rendererInfo ); - void Present() override; - - std::uint32_t GetCurrentSwapIndex() const override; - std::uint32_t GetNumSwapBuffers() const override; - std::uint32_t GetSamples() const override; - - Format GetColorFormat() const override; - Format GetDepthStencilFormat() const override; - - const RenderPass* GetRenderPass() const override; - - bool SetVsyncInterval(std::uint32_t vsyncInterval) override; - public: // Updates the native render pass descriptor with the specified clear values. Returns null on failure. diff --git a/sources/Renderer/Metal/MTSwapChain.mm b/sources/Renderer/Metal/MTSwapChain.mm index 5980ba1840..8e5ffd2efa 100644 --- a/sources/Renderer/Metal/MTSwapChain.mm +++ b/sources/Renderer/Metal/MTSwapChain.mm @@ -75,6 +75,11 @@ - (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size ShowSurface(); } +bool MTSwapChain::IsPresentable() const +{ + return true; //TODO +} + void MTSwapChain::Present() { /* Present backbuffer */ diff --git a/sources/Renderer/Null/NullSwapChain.cpp b/sources/Renderer/Null/NullSwapChain.cpp index a8a82d694b..01723ce82f 100644 --- a/sources/Renderer/Null/NullSwapChain.cpp +++ b/sources/Renderer/Null/NullSwapChain.cpp @@ -63,6 +63,11 @@ void NullSwapChain::SetDebugName(const char* name) label_.clear(); } +bool NullSwapChain::IsPresentable() const +{ + return true; // dummy +} + void NullSwapChain::Present() { // dummy diff --git a/sources/Renderer/Null/NullSwapChain.h b/sources/Renderer/Null/NullSwapChain.h index 468c80c911..8fde1547c5 100644 --- a/sources/Renderer/Null/NullSwapChain.h +++ b/sources/Renderer/Null/NullSwapChain.h @@ -22,28 +22,19 @@ class NullSwapChain final : public SwapChain public: - NullSwapChain( - const SwapChainDescriptor& desc, - const std::shared_ptr& surface, - const RendererInfo& rendererInfo - ); + #include public: void SetDebugName(const char* name) override; - void Present() override; - - std::uint32_t GetCurrentSwapIndex() const override; - std::uint32_t GetNumSwapBuffers() const override; - std::uint32_t GetSamples() const override; - - Format GetColorFormat() const override; - Format GetDepthStencilFormat() const override; - - bool SetVsyncInterval(std::uint32_t vsyncInterval) override; + public: - const RenderPass* GetRenderPass() const override; + NullSwapChain( + const SwapChainDescriptor& desc, + const std::shared_ptr& surface, + const RendererInfo& rendererInfo + ); private: diff --git a/sources/Renderer/OpenGL/GLSwapChain.cpp b/sources/Renderer/OpenGL/GLSwapChain.cpp index 7709ecc770..4aebe50ae1 100644 --- a/sources/Renderer/OpenGL/GLSwapChain.cpp +++ b/sources/Renderer/OpenGL/GLSwapChain.cpp @@ -11,6 +11,7 @@ #include "Platform/GLContextManager.h" #include #include +#include #ifdef LLGL_MOBILE_PLATFORM # include @@ -27,6 +28,19 @@ namespace LLGL { +static GLint GetFramebufferHeight(const Extent2D& resolution) +{ + #ifndef LLGL_OPENGL + /* For GLES, the framebuffer height is determined by the display height */ + if (LLGL::Display* display = LLGL::Display::GetPrimary()) + { + const Extent2D displayResolution = display->GetDisplayMode().resolution; + return static_cast(displayResolution.height); + } + #endif + return static_cast(resolution.height); +} + GLSwapChain::GLSwapChain( GLRenderSystem& renderSystem, const SwapChainDescriptor& desc, @@ -60,7 +74,7 @@ GLSwapChain::GLSwapChain( Cache resolution height after surface has been created, since high-resolution displays might provide a multiple of the input size. */ - framebufferHeight_ = static_cast(GetResolution().height); + framebufferHeight_ = GetFramebufferHeight(GetResolution()); /* Create platform dependent OpenGL context */ context_ = contextMngr.AllocContext(&pixelFormat, /*acceptCompatibleFormat:*/ false, &GetSurface()); @@ -79,6 +93,11 @@ GLSwapChain::GLSwapChain( } } +bool GLSwapChain::IsPresentable() const +{ + return swapChainContext_->HasDrawable(); +} + void GLSwapChain::Present() { swapChainContext_->SwapBuffers(); @@ -143,7 +162,7 @@ bool GLSwapChain::ResizeBuffersPrimary(const Extent2D& resolution) swapChainContext_->Resize(resolution); /* Update context height */ - const GLint height = static_cast(resolution.height); + const GLint height = GetFramebufferHeight(resolution); GetStateManager().ResetFramebufferHeight(height); framebufferHeight_ = height; diff --git a/sources/Renderer/OpenGL/GLSwapChain.h b/sources/Renderer/OpenGL/GLSwapChain.h index 3553d6e019..e1b5304814 100644 --- a/sources/Renderer/OpenGL/GLSwapChain.h +++ b/sources/Renderer/OpenGL/GLSwapChain.h @@ -33,7 +33,9 @@ class GLSwapChain final : public SwapChain public: - /* ----- Common ----- */ + #include + + public: GLSwapChain( GLRenderSystem& renderSystem, @@ -42,23 +44,6 @@ class GLSwapChain final : public SwapChain GLContextManager& contextMngr ); - void Present() override; - - std::uint32_t GetCurrentSwapIndex() const override; - std::uint32_t GetNumSwapBuffers() const override; - std::uint32_t GetSamples() const override; - - Format GetColorFormat() const override; - Format GetDepthStencilFormat() const override; - - const RenderPass* GetRenderPass() const override; - - bool SetVsyncInterval(std::uint32_t vsyncInterval) override; - - public: - - /* ----- GLSwapChain specific functions ----- */ - // Makes the swap-chain's GL context current and updates the renger-target height in the linked GL state manager. static bool MakeCurrent(GLSwapChain* swapChain); diff --git a/sources/Renderer/OpenGL/Platform/Android/AndroidGLSwapChainContext.cpp b/sources/Renderer/OpenGL/Platform/Android/AndroidGLSwapChainContext.cpp index d84994bb8b..0400185980 100644 --- a/sources/Renderer/OpenGL/Platform/Android/AndroidGLSwapChainContext.cpp +++ b/sources/Renderer/OpenGL/Platform/Android/AndroidGLSwapChainContext.cpp @@ -103,6 +103,11 @@ AndroidGLSwapChainContext::AndroidGLSwapChainContext(AndroidGLContext& context, CastTo(surface).AddEventListener(std::make_shared(this)); } +bool AndroidGLSwapChainContext::HasDrawable() const +{ + return (sharedSurface_->GetEGLSurface() != nullptr); +} + bool AndroidGLSwapChainContext::SwapBuffers() { eglSwapBuffers(display_, sharedSurface_->GetEGLSurface()); diff --git a/sources/Renderer/OpenGL/Platform/Android/AndroidGLSwapChainContext.h b/sources/Renderer/OpenGL/Platform/Android/AndroidGLSwapChainContext.h index 2923f725d9..94852144bd 100644 --- a/sources/Renderer/OpenGL/Platform/Android/AndroidGLSwapChainContext.h +++ b/sources/Renderer/OpenGL/Platform/Android/AndroidGLSwapChainContext.h @@ -29,6 +29,7 @@ class AndroidGLSwapChainContext final : public GLSwapChainContext AndroidGLSwapChainContext(AndroidGLContext& context, Surface& surface); + bool HasDrawable() const override; bool SwapBuffers() override; void Resize(const Extent2D& resolution) override; diff --git a/sources/Renderer/OpenGL/Platform/GLSwapChainContext.h b/sources/Renderer/OpenGL/Platform/GLSwapChainContext.h index 6a987ffd41..10d88a118f 100644 --- a/sources/Renderer/OpenGL/Platform/GLSwapChainContext.h +++ b/sources/Renderer/OpenGL/Platform/GLSwapChainContext.h @@ -31,6 +31,9 @@ class GLSwapChainContext virtual ~GLSwapChainContext() = default; + // Returns true if this swap-chain context has a drawable (e.g. EGLSurface) to render into. + virtual bool HasDrawable() const = 0; + // Swaps the back buffer with the front buffer (Win32: ::SwapBuffers, X11: glXSwapBuffers). virtual bool SwapBuffers() = 0; diff --git a/sources/Renderer/OpenGL/Platform/IOS/IOSGLSwapChainContext.h b/sources/Renderer/OpenGL/Platform/IOS/IOSGLSwapChainContext.h index 95409a5456..d6b5c6aac3 100644 --- a/sources/Renderer/OpenGL/Platform/IOS/IOSGLSwapChainContext.h +++ b/sources/Renderer/OpenGL/Platform/IOS/IOSGLSwapChainContext.h @@ -35,8 +35,8 @@ class IOSGLSwapChainContext final : public GLSwapChainContext IOSGLSwapChainContext(IOSGLContext& context, Surface& surface); + bool HasDrawable() const override; bool SwapBuffers() override; - void Resize(const Extent2D& resolution) override; public: diff --git a/sources/Renderer/OpenGL/Platform/IOS/IOSGLSwapChainContext.mm b/sources/Renderer/OpenGL/Platform/IOS/IOSGLSwapChainContext.mm index 845b13384b..0dc1936842 100644 --- a/sources/Renderer/OpenGL/Platform/IOS/IOSGLSwapChainContext.mm +++ b/sources/Renderer/OpenGL/Platform/IOS/IOSGLSwapChainContext.mm @@ -136,6 +136,11 @@ static GLKViewDrawableMultisample ToGLKViewMultisampleFormat(int samples) //[canvasView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[glkView]|" options:0 metrics:nil views:viewsDictionary]]; } +bool IOSGLSwapChainContext::HasDrawable() const +{ + return (view_ != nullptr); +} + bool IOSGLSwapChainContext::SwapBuffers() { return true; // dummy diff --git a/sources/Renderer/OpenGL/Platform/Linux/LinuxGLSwapChainContext.cpp b/sources/Renderer/OpenGL/Platform/Linux/LinuxGLSwapChainContext.cpp index bcfd936a02..5cbe4196c5 100644 --- a/sources/Renderer/OpenGL/Platform/Linux/LinuxGLSwapChainContext.cpp +++ b/sources/Renderer/OpenGL/Platform/Linux/LinuxGLSwapChainContext.cpp @@ -8,6 +8,7 @@ #include "LinuxGLSwapChainContext.h" #include "LinuxGLContext.h" #include "../../../../Core/CoreUtils.h" +#include "../../../../Core/Exception.h" #include @@ -46,7 +47,12 @@ LinuxGLSwapChainContext::LinuxGLSwapChainContext(LinuxGLContext& context, Surfac wnd_ = nativeHandle.window; } else - throw std::runtime_error("failed to get X11 Display and Window from swap-chain surface"); + LLGL_TRAP("failed to get X11 Display and Window from swap-chain surface"); +} + +bool LinuxGLSwapChainContext::HasDrawable() const +{ + return (wnd_ != 0); } bool LinuxGLSwapChainContext::SwapBuffers() diff --git a/sources/Renderer/OpenGL/Platform/Linux/LinuxGLSwapChainContext.h b/sources/Renderer/OpenGL/Platform/Linux/LinuxGLSwapChainContext.h index 58be33d20f..46190affa7 100644 --- a/sources/Renderer/OpenGL/Platform/Linux/LinuxGLSwapChainContext.h +++ b/sources/Renderer/OpenGL/Platform/Linux/LinuxGLSwapChainContext.h @@ -28,6 +28,7 @@ class LinuxGLSwapChainContext final : public GLSwapChainContext LinuxGLSwapChainContext(LinuxGLContext& context, Surface& surface); + bool HasDrawable() const override; bool SwapBuffers() override; void Resize(const Extent2D& resolution) override; diff --git a/sources/Renderer/OpenGL/Platform/MacOS/MacOSGLSwapChainContext.h b/sources/Renderer/OpenGL/Platform/MacOS/MacOSGLSwapChainContext.h index 16442696e3..945da754f6 100644 --- a/sources/Renderer/OpenGL/Platform/MacOS/MacOSGLSwapChainContext.h +++ b/sources/Renderer/OpenGL/Platform/MacOS/MacOSGLSwapChainContext.h @@ -29,8 +29,8 @@ class MacOSGLSwapChainContext final : public GLSwapChainContext MacOSGLSwapChainContext(MacOSGLContext& context, Surface& surface); + bool HasDrawable() const override; bool SwapBuffers() override; - void Resize(const Extent2D& resolution) override; public: diff --git a/sources/Renderer/OpenGL/Platform/MacOS/MacOSGLSwapChainContext.mm b/sources/Renderer/OpenGL/Platform/MacOS/MacOSGLSwapChainContext.mm index 148f6f45e7..e4d65133f1 100644 --- a/sources/Renderer/OpenGL/Platform/MacOS/MacOSGLSwapChainContext.mm +++ b/sources/Renderer/OpenGL/Platform/MacOS/MacOSGLSwapChainContext.mm @@ -50,6 +50,11 @@ LLGL_TRAP("NativeHandle::responder is neither of type NSWindow nor NSView for GLKView"); } +bool MacOSGLSwapChainContext::HasDrawable() const +{ + return (view_ != nullptr); +} + bool MacOSGLSwapChainContext::SwapBuffers() { [ctx_ flushBuffer]; diff --git a/sources/Renderer/OpenGL/Platform/Win32/Win32GLSwapChainContext.cpp b/sources/Renderer/OpenGL/Platform/Win32/Win32GLSwapChainContext.cpp index bb628be91a..42b8a06e88 100644 --- a/sources/Renderer/OpenGL/Platform/Win32/Win32GLSwapChainContext.cpp +++ b/sources/Renderer/OpenGL/Platform/Win32/Win32GLSwapChainContext.cpp @@ -8,6 +8,7 @@ #include "Win32GLSwapChainContext.h" #include "Win32GLContext.h" #include "../../../../Core/CoreUtils.h" +#include "../../../../Core/Exception.h" #include @@ -43,13 +44,18 @@ Win32GLSwapChainContext::Win32GLSwapChainContext(Win32GLContext& context, Surfac if (surface.GetNativeHandle(&nativeHandle, sizeof(nativeHandle))) hDC_ = ::GetDC(nativeHandle.window); else - throw std::runtime_error("failed to get Win32 device context (HDC) from swap-chain surface"); + LLGL_TRAP("failed to get Win32 device context (HDC) from swap-chain surface"); /* Select pixel format for device context if the GL context was originally created with a different surface */ if (context.GetDCHandle() != hDC_) context.SelectPixelFormat(hDC_); } +bool Win32GLSwapChainContext::HasDrawable() const +{ + return (hDC_ != nullptr); +} + bool Win32GLSwapChainContext::SwapBuffers() { return (::SwapBuffers(hDC_) != FALSE); diff --git a/sources/Renderer/OpenGL/Platform/Win32/Win32GLSwapChainContext.h b/sources/Renderer/OpenGL/Platform/Win32/Win32GLSwapChainContext.h index 51be6be92b..414b1fc033 100644 --- a/sources/Renderer/OpenGL/Platform/Win32/Win32GLSwapChainContext.h +++ b/sources/Renderer/OpenGL/Platform/Win32/Win32GLSwapChainContext.h @@ -27,6 +27,7 @@ class Win32GLSwapChainContext final : public GLSwapChainContext Win32GLSwapChainContext(Win32GLContext& context, Surface& surface); + bool HasDrawable() const override; bool SwapBuffers() override; void Resize(const Extent2D& resolution) override; @@ -36,8 +37,8 @@ class Win32GLSwapChainContext final : public GLSwapChainContext private: - HGLRC hGLRC_; - HDC hDC_; + HGLRC hGLRC_ = nullptr; + HDC hDC_ = nullptr; }; diff --git a/sources/Renderer/Vulkan/VKSwapChain.cpp b/sources/Renderer/Vulkan/VKSwapChain.cpp index eff9923e20..070354f944 100644 --- a/sources/Renderer/Vulkan/VKSwapChain.cpp +++ b/sources/Renderer/Vulkan/VKSwapChain.cpp @@ -104,6 +104,12 @@ VKSwapChain::VKSwapChain( ShowSurface(); } +bool VKSwapChain::IsPresentable() const +{ + /* Use implicit boolean conversion here, since this type can either be a pointer or an integer type depending on the platform */ + return !!surface_.Get(); +} + void VKSwapChain::Present() { /* Initialize semaphores */ diff --git a/sources/Renderer/Vulkan/VKSwapChain.h b/sources/Renderer/Vulkan/VKSwapChain.h index 52a25bcd4e..b453b6b596 100644 --- a/sources/Renderer/Vulkan/VKSwapChain.h +++ b/sources/Renderer/Vulkan/VKSwapChain.h @@ -32,6 +32,10 @@ class VKDeviceMemoryRegion; class VKSwapChain final : public SwapChain { + public: + + #include + public: VKSwapChain( @@ -44,21 +48,6 @@ class VKSwapChain final : public SwapChain const RendererInfo& rendererInfo ); - void Present() override; - - std::uint32_t GetCurrentSwapIndex() const override; - std::uint32_t GetNumSwapBuffers() const override; - std::uint32_t GetSamples() const override; - - Format GetColorFormat() const override; - Format GetDepthStencilFormat() const override; - - const RenderPass* GetRenderPass() const override; - - bool SetVsyncInterval(std::uint32_t vsyncInterval) override; - - public: - // Returns the swap-chain render pass object. inline const VKRenderPass& GetSwapChainRenderPass() const { diff --git a/wrapper/C99/C99SwapChain.cpp b/wrapper/C99/C99SwapChain.cpp index 1e396e8884..897eda3ca3 100644 --- a/wrapper/C99/C99SwapChain.cpp +++ b/wrapper/C99/C99SwapChain.cpp @@ -15,6 +15,11 @@ using namespace LLGL; +LLGL_C_EXPORT bool llglIsPresentable(LLGLSwapChain swapChain) +{ + return LLGL_PTR(SwapChain, swapChain)->IsPresentable(); +} + LLGL_C_EXPORT void llglPresent(LLGLSwapChain swapChain) { LLGL_PTR(SwapChain, swapChain)->Present(); diff --git a/wrapper/CSharp/LLGLWrapper.cs b/wrapper/CSharp/LLGLWrapper.cs index d992d68eda..8563b1b5d0 100644 --- a/wrapper/CSharp/LLGLWrapper.cs +++ b/wrapper/CSharp/LLGLWrapper.cs @@ -4774,6 +4774,10 @@ public unsafe struct ShaderReflection [DllImport(DllName, EntryPoint="llglResetSurfacePixelFormat", CallingConvention=CallingConvention.Cdecl)] public static extern unsafe void ResetSurfacePixelFormat(Surface surface); + [DllImport(DllName, EntryPoint="llglIsPresentable", CallingConvention=CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + public static extern unsafe bool IsPresentable(SwapChain swapChain); + [DllImport(DllName, EntryPoint="llglPresent", CallingConvention=CallingConvention.Cdecl)] public static extern unsafe void Present(SwapChain swapChain); diff --git a/wrapper/CSharp/SwapChain.cs b/wrapper/CSharp/SwapChain.cs index 64ec6106ce..b1749958ad 100644 --- a/wrapper/CSharp/SwapChain.cs +++ b/wrapper/CSharp/SwapChain.cs @@ -33,6 +33,14 @@ public Surface Surface } } + public bool IsPresentable + { + get + { + return NativeLLGL.IsPresentable(NativeSub); + } + } + public void Present() { NativeLLGL.Present(NativeSub);