diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index 969632c327a..411aa65a99d 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -24,7 +24,6 @@ #include -#include #include #include @@ -1225,7 +1224,7 @@ static_assert(sizeof(StencilState::StencilOperations) == 5u, static_assert(sizeof(StencilState) == 12u, "StencilState size not what was intended"); -using FrameScheduledCallback = utils::Invocable; +using FrameScheduledCallback = void(*)(PresentCallable callable, void* user); enum class Workaround : uint16_t { // The EASU pass must split because shader compiler flattens early-exit branch diff --git a/filament/backend/include/backend/PresentCallable.h b/filament/backend/include/backend/PresentCallable.h index f37d7704b49..4402f22266d 100644 --- a/filament/backend/include/backend/PresentCallable.h +++ b/filament/backend/include/backend/PresentCallable.h @@ -48,7 +48,7 @@ namespace filament::backend { * and optional user data: * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * swapChain->setFrameScheduledCallback(nullptr, myFrameScheduledCallback); + * swapChain->setFrameScheduledCallback(myFrameScheduledCallback, nullptr); * if (renderer->beginFrame(swapChain)) { * renderer->render(view); * renderer->endFrame(); @@ -58,6 +58,8 @@ namespace filament::backend { * @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other * backends ignore the callback (which will never be called) and proceed normally. * + * @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread. + * * Applications *must* call each PresentCallable they receive. Each PresentCallable represents a * frame that is waiting to be presented. If an application fails to call a PresentCallable, a * memory leak could occur. To "cancel" the presentation of a frame, pass false to the diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index f729a370258..2e2f81d9d37 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -138,8 +138,8 @@ DECL_DRIVER_API_N(beginFrame, DECL_DRIVER_API_N(setFrameScheduledCallback, backend::SwapChainHandle, sch, - backend::CallbackHandler*, handler, - backend::FrameScheduledCallback&&, callback) + backend::FrameScheduledCallback, callback, + void*, user) DECL_DRIVER_API_N(setFrameCompletedCallback, backend::SwapChainHandle, sch, diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 039754e7f38..c2c5680ae30 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -237,10 +237,10 @@ } } -void MetalDriver::setFrameScheduledCallback( - Handle sch, CallbackHandler* handler, FrameScheduledCallback&& callback) { +void MetalDriver::setFrameScheduledCallback(Handle sch, + FrameScheduledCallback callback, void* user) { auto* swapChain = handle_cast(sch); - swapChain->setFrameScheduledCallback(handler, std::move(callback)); + swapChain->setFrameScheduledCallback(callback, user); } void MetalDriver::setFrameCompletedCallback(Handle sch, diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index c6c65e1f7d6..fea6f0947f9 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -31,8 +31,6 @@ #include "private/backend/SamplerGroup.h" -#include - #include #include #include @@ -73,9 +71,9 @@ class MetalSwapChain : public HwSwapChain { void releaseDrawable(); - void setFrameScheduledCallback(CallbackHandler* handler, FrameScheduledCallback&& callback); - void setFrameCompletedCallback( - CallbackHandler* handler, CallbackHandler::Callback callback, void* user); + void setFrameScheduledCallback(FrameScheduledCallback callback, void* user); + void setFrameCompletedCallback(CallbackHandler* handler, + CallbackHandler::Callback callback, void* user); // For CAMetalLayer-backed SwapChains, presents the drawable or schedules a // FrameScheduledCallback. @@ -112,15 +110,14 @@ class MetalSwapChain : public HwSwapChain { MetalExternalImage externalImage; SwapChainType type; - // These fields store a callback to notify the client that a frame is ready for presentation. If - // !frameScheduled.callback, then the Metal backend automatically calls presentDrawable when the - // frame is committed. Otherwise, the Metal backend will not automatically present the frame. - // Instead, clients bear the responsibility of presenting the frame by calling the - // PresentCallable object. - struct { - CallbackHandler* handler = nullptr; - FrameScheduledCallback callback = {}; - } frameScheduled; + // These two fields store a callback and user data to notify the client that a frame is ready + // for presentation. + // If frameScheduledCallback is nullptr, then the Metal backend automatically calls + // presentDrawable when the frame is committed. + // Otherwise, the Metal backend will not automatically present the frame. Instead, clients bear + // the responsibility of presenting the frame by calling the PresentCallable object. + FrameScheduledCallback frameScheduledCallback = nullptr; + void* frameScheduledUserData = nullptr; struct { CallbackHandler* handler = nullptr; diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 15958ab60ef..18d38ad74ed 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -221,10 +221,9 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) { depthStencilTexture = [context.device newTextureWithDescriptor:descriptor]; } -void MetalSwapChain::setFrameScheduledCallback( - CallbackHandler* handler, FrameScheduledCallback&& callback) { - frameScheduled.handler = handler; - frameScheduled.callback = std::move(callback); +void MetalSwapChain::setFrameScheduledCallback(FrameScheduledCallback callback, void* user) { + frameScheduledCallback = callback; + frameScheduledUserData = user; } void MetalSwapChain::setFrameCompletedCallback(CallbackHandler* handler, @@ -239,7 +238,7 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) { scheduleFrameCompletedCallback(); } if (drawable) { - if (frameScheduled.callback) { + if (frameScheduledCallback) { scheduleFrameScheduledCallback(); } else { [getPendingCommandBuffer(&context) presentDrawable:drawable]; @@ -297,38 +296,21 @@ void presentDrawable(bool presentFrame, void* user) { } void MetalSwapChain::scheduleFrameScheduledCallback() { - if (!frameScheduled.callback) { + if (!frameScheduledCallback) { return; } assert_invariant(drawable); - struct Callback { - Callback(FrameScheduledCallback&& callback, id drawable, - MetalDriver* driver) - : f(std::move(callback)), data(PresentDrawableData::create(drawable, driver)) {} - FrameScheduledCallback f; - // PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later. - std::unique_ptr data; - static void func(void* user) { - auto* const c = reinterpret_cast(user); - PresentDrawableData* presentDrawableData = c->data.release(); - PresentCallable presentCallable(presentDrawable, presentDrawableData); - c->f(presentCallable); - delete c; - } - }; + // Destroy this by calling maybePresentAndDestroyAsync() later. + auto* presentData = PresentDrawableData::create(drawable, context.driver); - // This callback pointer will be captured by the block. Even if the scheduled handler is never - // called, the unique_ptr will still ensure we don't leak memory. - __block auto callback = - std::make_unique(std::move(frameScheduled.callback), drawable, context.driver); + FrameScheduledCallback userCallback = frameScheduledCallback; + void* userData = frameScheduledUserData; - backend::CallbackHandler* handler = frameScheduled.handler; - MetalDriver* driver = context.driver; [getPendingCommandBuffer(&context) addScheduledHandler:^(id cb) { - Callback* user = callback.release(); - driver->scheduleCallback(handler, user, &Callback::func); + PresentCallable callable(presentDrawable, static_cast(presentData)); + userCallback(callable, userData); }]; } diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 9984bed9a68..911f967413d 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -54,7 +54,7 @@ void NoopDriver::beginFrame(int64_t monotonic_clock_ns, } void NoopDriver::setFrameScheduledCallback(Handle sch, - CallbackHandler* handler, FrameScheduledCallback&& callback) { + FrameScheduledCallback callback, void* user) { } diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 52b75fcf166..7583b2e460d 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -3417,7 +3417,7 @@ void OpenGLDriver::beginFrame( } void OpenGLDriver::setFrameScheduledCallback(Handle sch, - CallbackHandler* handler, FrameScheduledCallback&& callback) { + FrameScheduledCallback callback, void* user) { DEBUG_MARKER() } diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 49e6f581c9f..1c2dd8472d9 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -394,7 +394,7 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, } void VulkanDriver::setFrameScheduledCallback(Handle sch, - CallbackHandler* handler, FrameScheduledCallback&& callback) { + FrameScheduledCallback callback, void* user) { } void VulkanDriver::setFrameCompletedCallback(Handle sch, diff --git a/filament/include/filament/SwapChain.h b/filament/include/filament/SwapChain.h index 585e016eec0..0af01afc966 100644 --- a/filament/include/filament/SwapChain.h +++ b/filament/include/filament/SwapChain.h @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -265,22 +264,13 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { * backend. * * A FrameScheduledCallback can be set on an individual SwapChain through - * SwapChain::setFrameScheduledCallback. If the callback is set for a given frame, then the - * SwapChain will *not* automatically schedule itself for presentation. Instead, the application - * must call the PresentCallable passed to the FrameScheduledCallback. + * SwapChain::setFrameScheduledCallback. If the callback is set, then the SwapChain will *not* + * automatically schedule itself for presentation. Instead, the application must call the + * PresentCallable passed to the FrameScheduledCallback. * - * Each SwapChain can have only one FrameScheduledCallback set per frame. If - * setFrameScheduledCallback is called multiple times on the same SwapChain before - * Renderer::endFrame(), the most recent call effectively overwrites any previously set - * callback. This allows the callback to be updated as needed before the frame has finished - * encoding. - * - * The "last" callback set by setFrameScheduledCallback gets "latched" when Renderer::endFrame() - * is executed. At this point, the state of the callback is fixed and is the one used for the - * frame that was just encoded. Subsequent changes to the callback using - * setFrameScheduledCallback after endFrame() apply to the next frame. - * - * Use \c setFrameScheduledCallback() (with default arguments) to unset the callback. + * There may be only one FrameScheduledCallback set per SwapChain. A call to + * SwapChain::setFrameScheduledCallback will overwrite any previous FrameScheduledCallbacks set + * on the same SwapChain. * * If your application delays the call to the PresentCallable by, for example, calling it on a * separate thread, you must ensure all PresentCallables have been called before shutting down @@ -288,26 +278,28 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI { * Engine::shutdown. This is necessary to ensure the Filament Engine has had a chance to clean * up all memory related to frame presentation. * - * @param handler Handler to dispatch the callback or nullptr for the default handler. - * @param callback Callback called when the frame is scheduled. + * @param callback A callback, or nullptr to unset. + * @param user An optional pointer to user data passed to the callback function. * * @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other * backends ignore the callback (which will never be called) and proceed normally. * - * @see CallbackHandler + * @remark The SwapChain::FrameScheduledCallback is called on an arbitrary thread. + * * @see PresentCallable */ - void setFrameScheduledCallback(backend::CallbackHandler* UTILS_NULLABLE handler = nullptr, - FrameScheduledCallback&& callback = {}); + void setFrameScheduledCallback(FrameScheduledCallback UTILS_NULLABLE callback, + void* UTILS_NULLABLE user = nullptr); /** - * Returns whether or not this SwapChain currently has a FrameScheduledCallback set. + * Returns the SwapChain::FrameScheduledCallback that was previously set with + * SwapChain::setFrameScheduledCallback, or nullptr if one is not set. * - * @return true, if the last call to setFrameScheduledCallback set a callback + * @return the previously-set FrameScheduledCallback, or nullptr * * @see SwapChain::setFrameCompletedCallback */ - bool isFrameScheduledCallbackSet() const noexcept; + UTILS_NULLABLE FrameScheduledCallback getFrameScheduledCallback() const noexcept; /** * FrameCompletedCallback is a callback function that notifies an application when a frame's diff --git a/filament/src/SwapChain.cpp b/filament/src/SwapChain.cpp index dd7db7bd011..a242ef06ccb 100644 --- a/filament/src/SwapChain.cpp +++ b/filament/src/SwapChain.cpp @@ -28,13 +28,12 @@ void* SwapChain::getNativeWindow() const noexcept { return downcast(this)->getNativeWindow(); } -void SwapChain::setFrameScheduledCallback( - backend::CallbackHandler* handler, FrameScheduledCallback&& callback) { - downcast(this)->setFrameScheduledCallback(handler, std::move(callback)); +void SwapChain::setFrameScheduledCallback(FrameScheduledCallback callback, void* user) { + downcast(this)->setFrameScheduledCallback(callback, user); } -bool SwapChain::isFrameScheduledCallbackSet() const noexcept { - return downcast(this)->isFrameScheduledCallbackSet(); +SwapChain::FrameScheduledCallback SwapChain::getFrameScheduledCallback() const noexcept { + return downcast(this)->getFrameScheduledCallback(); } void SwapChain::setFrameCompletedCallback(backend::CallbackHandler* handler, diff --git a/filament/src/details/SwapChain.cpp b/filament/src/details/SwapChain.cpp index 0407d893763..ef4eb4fabd4 100644 --- a/filament/src/details/SwapChain.cpp +++ b/filament/src/details/SwapChain.cpp @@ -69,14 +69,13 @@ void FSwapChain::terminate(FEngine& engine) noexcept { engine.getDriverApi().destroySwapChain(mHwSwapChain); } -void FSwapChain::setFrameScheduledCallback( - backend::CallbackHandler* handler, FrameScheduledCallback&& callback) { - mFrameScheduledCallbackIsSet = bool(callback); - mEngine.getDriverApi().setFrameScheduledCallback(mHwSwapChain, handler, std::move(callback)); +void FSwapChain::setFrameScheduledCallback(FrameScheduledCallback callback, void* user) { + mFrameScheduledCallback = callback; + mEngine.getDriverApi().setFrameScheduledCallback(mHwSwapChain, callback, user); } -bool FSwapChain::isFrameScheduledCallbackSet() const noexcept { - return mFrameScheduledCallbackIsSet; +SwapChain::FrameScheduledCallback FSwapChain::getFrameScheduledCallback() const noexcept { + return mFrameScheduledCallback; } void FSwapChain::setFrameCompletedCallback(backend::CallbackHandler* handler, diff --git a/filament/src/details/SwapChain.h b/filament/src/details/SwapChain.h index efe7483563e..7a97727e832 100644 --- a/filament/src/details/SwapChain.h +++ b/filament/src/details/SwapChain.h @@ -78,10 +78,9 @@ class FSwapChain : public SwapChain { return mHwSwapChain; } - void setFrameScheduledCallback( - backend::CallbackHandler* handler, FrameScheduledCallback&& callback); + void setFrameScheduledCallback(FrameScheduledCallback callback, void* user); - bool isFrameScheduledCallbackSet() const noexcept; + FrameScheduledCallback getFrameScheduledCallback() const noexcept; void setFrameCompletedCallback(backend::CallbackHandler* handler, utils::Invocable&& callback) noexcept; @@ -97,7 +96,7 @@ class FSwapChain : public SwapChain { private: FEngine& mEngine; backend::Handle mHwSwapChain; - bool mFrameScheduledCallbackIsSet = false; + FrameScheduledCallback mFrameScheduledCallback{}; void* mNativeWindow{}; uint32_t mWidth{}; uint32_t mHeight{};