Skip to content

Commit b97221b

Browse files
authored
Improve setFrameScheduled threading (#8139)
1 parent 3f37efe commit b97221b

File tree

12 files changed

+68
-31
lines changed

12 files changed

+68
-31
lines changed

filament/backend/include/private/backend/DriverAPI.inc

+2-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ DECL_DRIVER_API_N(beginFrame,
139139
DECL_DRIVER_API_N(setFrameScheduledCallback,
140140
backend::SwapChainHandle, sch,
141141
backend::CallbackHandler*, handler,
142-
backend::FrameScheduledCallback&&, callback)
142+
backend::FrameScheduledCallback&&, callback,
143+
uint64_t, flags)
143144

144145
DECL_DRIVER_API_N(setFrameCompletedCallback,
145146
backend::SwapChainHandle, sch,

filament/backend/src/metal/MetalDriver.mm

+9-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#include "CommandStreamDispatcher.h"
2020
#include "metal/MetalDriver.h"
2121

22+
#include <filament/SwapChain.h>
23+
2224
#include "MetalBlitter.h"
2325
#include "MetalBufferPool.h"
2426
#include "MetalContext.h"
@@ -240,10 +242,14 @@
240242
}
241243
}
242244

243-
void MetalDriver::setFrameScheduledCallback(
244-
Handle<HwSwapChain> sch, CallbackHandler* handler, FrameScheduledCallback&& callback) {
245+
void MetalDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
246+
FrameScheduledCallback&& callback, uint64_t flags) {
247+
// Turn off the CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER flag if a custom handler is provided.
248+
if (handler) {
249+
flags &= ~SwapChain::CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER;
250+
}
245251
auto* swapChain = handle_cast<MetalSwapChain>(sch);
246-
swapChain->setFrameScheduledCallback(handler, std::move(callback));
252+
swapChain->setFrameScheduledCallback(handler, std::move(callback), flags);
247253
}
248254

249255
void MetalDriver::setFrameCompletedCallback(

filament/backend/src/metal/MetalHandles.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ class MetalSwapChain : public HwSwapChain {
7373

7474
void releaseDrawable();
7575

76-
void setFrameScheduledCallback(CallbackHandler* handler, FrameScheduledCallback&& callback);
76+
void setFrameScheduledCallback(
77+
CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags);
7778
void setFrameCompletedCallback(
7879
CallbackHandler* handler, utils::Invocable<void(void)>&& callback);
7980

@@ -121,6 +122,7 @@ class MetalSwapChain : public HwSwapChain {
121122
struct {
122123
CallbackHandler* handler = nullptr;
123124
std::shared_ptr<FrameScheduledCallback> callback = nullptr;
125+
uint64_t flags = 0;
124126
} frameScheduled;
125127

126128
struct {

filament/backend/src/metal/MetalHandles.mm

+27-13
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,10 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
233233
}
234234

235235
void MetalSwapChain::setFrameScheduledCallback(
236-
CallbackHandler* handler, FrameScheduledCallback&& callback) {
236+
CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags) {
237237
frameScheduled.handler = handler;
238238
frameScheduled.callback = std::make_shared<FrameScheduledCallback>(std::move(callback));
239+
frameScheduled.flags = flags;
239240
}
240241

241242
void MetalSwapChain::setFrameCompletedCallback(
@@ -264,27 +265,34 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
264265
PresentDrawableData& operator=(const PresentDrawableData&) = delete;
265266

266267
static PresentDrawableData* create(id<CAMetalDrawable> drawable,
267-
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver) {
268+
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags) {
268269
assert_invariant(drawableMutex);
269270
assert_invariant(driver);
270-
return new PresentDrawableData(drawable, drawableMutex, driver);
271+
return new PresentDrawableData(drawable, drawableMutex, driver, flags);
271272
}
272273

273274
static void maybePresentAndDestroyAsync(PresentDrawableData* that, bool shouldPresent) {
274275
if (shouldPresent) {
275276
[that->mDrawable present];
276277
}
277278

278-
// mDrawable is acquired on the driver thread. Typically, we would release this object on
279-
// the same thread, but after receiving consistent crash reports from within
280-
// [CAMetalDrawable dealloc], we suspect this object requires releasing on the main thread.
281-
dispatch_async(dispatch_get_main_queue(), ^{ cleanupAndDestroy(that); });
279+
if (that->mFlags & SwapChain::CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER) {
280+
cleanupAndDestroy(that);
281+
} else {
282+
// mDrawable is acquired on the driver thread. Typically, we would release this object
283+
// on the same thread, but after receiving consistent crash reports from within
284+
// [CAMetalDrawable dealloc], we suspect this object requires releasing on the main
285+
// thread.
286+
dispatch_async(dispatch_get_main_queue(), ^{
287+
cleanupAndDestroy(that);
288+
});
289+
}
282290
}
283291

284292
private:
285293
PresentDrawableData(id<CAMetalDrawable> drawable, std::shared_ptr<std::mutex> drawableMutex,
286-
MetalDriver* driver)
287-
: mDrawable(drawable), mDrawableMutex(drawableMutex), mDriver(driver) {}
294+
MetalDriver* driver, uint64_t flags)
295+
: mDrawable(drawable), mDrawableMutex(drawableMutex), mDriver(driver), mFlags(flags) {}
288296

289297
static void cleanupAndDestroy(PresentDrawableData *that) {
290298
if (that->mDrawable) {
@@ -299,6 +307,7 @@ static void cleanupAndDestroy(PresentDrawableData *that) {
299307
id<CAMetalDrawable> mDrawable;
300308
std::shared_ptr<std::mutex> mDrawableMutex;
301309
MetalDriver* mDriver = nullptr;
310+
uint64_t mFlags = 0;
302311
};
303312

304313
void presentDrawable(bool presentFrame, void* user) {
@@ -315,8 +324,8 @@ void presentDrawable(bool presentFrame, void* user) {
315324

316325
struct Callback {
317326
Callback(std::shared_ptr<FrameScheduledCallback> callback, id<CAMetalDrawable> drawable,
318-
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver)
319-
: f(callback), data(PresentDrawableData::create(drawable, drawableMutex, driver)) {}
327+
std::shared_ptr<std::mutex> drawableMutex, MetalDriver* driver, uint64_t flags)
328+
: f(callback), data(PresentDrawableData::create(drawable, drawableMutex, driver, flags)) {}
320329
std::shared_ptr<FrameScheduledCallback> f;
321330
// PresentDrawableData* is destroyed by maybePresentAndDestroyAsync() later.
322331
std::unique_ptr<PresentDrawableData> data;
@@ -331,14 +340,19 @@ static void func(void* user) {
331340

332341
// This callback pointer will be captured by the block. Even if the scheduled handler is never
333342
// called, the unique_ptr will still ensure we don't leak memory.
343+
uint64_t const flags = frameScheduled.flags;
334344
__block auto callback = std::make_unique<Callback>(
335-
frameScheduled.callback, drawable, layerDrawableMutex, context.driver);
345+
frameScheduled.callback, drawable, layerDrawableMutex, context.driver, flags);
336346

337347
backend::CallbackHandler* handler = frameScheduled.handler;
338348
MetalDriver* driver = context.driver;
339349
[getPendingCommandBuffer(&context) addScheduledHandler:^(id<MTLCommandBuffer> cb) {
340350
Callback* user = callback.release();
341-
driver->scheduleCallback(handler, user, &Callback::func);
351+
if (flags & SwapChain::CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER) {
352+
Callback::func(user);
353+
} else {
354+
driver->scheduleCallback(handler, user, &Callback::func);
355+
}
342356
}];
343357
}
344358

filament/backend/src/noop/NoopDriver.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void NoopDriver::beginFrame(int64_t monotonic_clock_ns,
5454
}
5555

5656
void NoopDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
57-
CallbackHandler* handler, FrameScheduledCallback&& callback) {
57+
CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags) {
5858

5959
}
6060

filament/backend/src/opengl/OpenGLDriver.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -3528,8 +3528,8 @@ void OpenGLDriver::beginFrame(
35283528
}
35293529
}
35303530

3531-
void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
3532-
CallbackHandler* handler, FrameScheduledCallback&& callback) {
3531+
void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
3532+
FrameScheduledCallback&& callback, uint64_t flags) {
35333533
DEBUG_MARKER()
35343534
}
35353535

filament/backend/src/vulkan/VulkanDriver.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -403,8 +403,8 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
403403
FVK_SYSTRACE_END();
404404
}
405405

406-
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
407-
CallbackHandler* handler, FrameScheduledCallback&& callback) {
406+
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
407+
FrameScheduledCallback&& callback, uint64_t flags) {
408408
}
409409

410410
void VulkanDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,

filament/backend/test/test_Callbacks.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ TEST_F(BackendTest, FrameScheduledCallback) {
3535
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
3636
callable();
3737
callbackCountA++;
38-
});
38+
}, 0);
3939

4040
// Render the first frame.
4141
api.makeCurrent(swapChain, swapChain);
@@ -58,7 +58,7 @@ TEST_F(BackendTest, FrameScheduledCallback) {
5858
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountB](PresentCallable callable) {
5959
callable();
6060
callbackCountB++;
61-
});
61+
}, 0);
6262

6363
// Render one final frame.
6464
api.makeCurrent(swapChain, swapChain);

filament/include/filament/SwapChain.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,19 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI {
254254

255255
void* UTILS_NULLABLE getNativeWindow() const noexcept;
256256

257+
/**
258+
* If this flag is passed to setFrameScheduledCallback, then the behavior of the default
259+
* CallbackHandler (when nullptr is passed as the handler argument) is altered to call the
260+
* callback on the Metal completion handler thread (as opposed to the main Filament thread).
261+
* This flag also instructs the Metal backend to release the associated CAMetalDrawable on the
262+
* completion handler thread.
263+
*
264+
* This flag has no effect if a custom CallbackHandler is passed.
265+
*
266+
* @see setFrameScheduledCallback
267+
*/
268+
static constexpr uint64_t CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER = 1;
269+
257270
/**
258271
* FrameScheduledCallback is a callback function that notifies an application when Filament has
259272
* completed processing a frame and that frame is ready to be scheduled for presentation.
@@ -298,7 +311,7 @@ class UTILS_PUBLIC SwapChain : public FilamentAPI {
298311
* @see PresentCallable
299312
*/
300313
void setFrameScheduledCallback(backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
301-
FrameScheduledCallback&& callback = {});
314+
FrameScheduledCallback&& callback = {}, uint64_t flags = 0);
302315

303316
/**
304317
* Returns whether or not this SwapChain currently has a FrameScheduledCallback set.

filament/src/SwapChain.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ void* SwapChain::getNativeWindow() const noexcept {
2929
}
3030

3131
void SwapChain::setFrameScheduledCallback(
32-
backend::CallbackHandler* handler, FrameScheduledCallback&& callback) {
33-
downcast(this)->setFrameScheduledCallback(handler, std::move(callback));
32+
backend::CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags) {
33+
downcast(this)->setFrameScheduledCallback(handler, std::move(callback), flags);
3434
}
3535

3636
bool SwapChain::isFrameScheduledCallbackSet() const noexcept {

filament/src/details/SwapChain.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ void FSwapChain::terminate(FEngine& engine) noexcept {
7070
}
7171

7272
void FSwapChain::setFrameScheduledCallback(
73-
backend::CallbackHandler* handler, FrameScheduledCallback&& callback) {
73+
backend::CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags) {
7474
mFrameScheduledCallbackIsSet = bool(callback);
75-
mEngine.getDriverApi().setFrameScheduledCallback(mHwSwapChain, handler, std::move(callback));
75+
mEngine.getDriverApi().setFrameScheduledCallback(
76+
mHwSwapChain, handler, std::move(callback), flags);
7677
}
7778

7879
bool FSwapChain::isFrameScheduledCallbackSet() const noexcept {

filament/src/details/SwapChain.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class FSwapChain : public SwapChain {
7979
}
8080

8181
void setFrameScheduledCallback(
82-
backend::CallbackHandler* handler, FrameScheduledCallback&& callback);
82+
backend::CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags);
8383

8484
bool isFrameScheduledCallbackSet() const noexcept;
8585

0 commit comments

Comments
 (0)