Repository: google/filament
File: filament/backend/src/metal/MetalDriver.mm (Objective-C++)

Code before:
    }
}

void MetalDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
        FrameScheduledCallback&& callback, uint64_t flags) {
    // Turn off the CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER flag if a custom handler is provided.
    if (handler) {
        flags &= ~SwapChain::CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER;
    }
    auto* swapChain = handle_cast<MetalSwapChain>(sch);
    swapChain->setFrameScheduledCallback(handler, std::move(callback), flags);
}

void MetalDriver::setFrameCompletedCallback(
        Handle<HwSwapChain> sch, CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
    auto* swapChain = handle_cast<MetalSwapChain>(sch);
    swapChain->setFrameCompletedCallback(handler, std::move(callback));
}

void MetalDriver::execute(std::function<void(void)> const& fn) noexcept {
    @autoreleasepool {
        fn();
    }
}

void MetalDriver::setPresentationTime(int64_t monotonic_clock_ns) {
    mContext->presentationTimeNs = monotonic_clock_ns;
}

void MetalDriver::endFrame(uint32_t frameId) {
    DEBUG_LOG("endFrame(frameId = %d)\n", frameId);
    // If we haven't committed the command buffer (if the frame was canceled), do it now. There may
    // be commands in it (like fence signaling) that need to execute.
    submitPendingCommands(mContext);

    mContext->bufferPool->gc();

    // If we acquired a drawable for this frame, ensure that we release it here.
    if (mContext->currentDrawSwapChain) {
        mContext->currentDrawSwapChain->releaseDrawable();
    }

    CVMetalTextureCacheFlush(mContext->textureCache, 0);

    assert_invariant(mContext->groupMarkers.empty());

    // If we exceeded memoryless limits, turn it off for the rest of the lifetime of the driver.
    if (mContext->supportsMemorylessRenderTargets && mContext->memorylessLimitsReached) {
        for (MetalTexture* texture : mContext->textures) {
            // Release memoryless MTLTexture-s, which are currently only the MSAA sidecars.
            // Creation of new render targets is going to trigger the re-allocation of sidecars,

Review comment (bug):
`Renderer::setPresentationTime()` must be called between beginFrame/enFrame, and this is because it acts on the current swapchain, i.e. the one set in `makeCurrent`.

So the "presentationTime" cannot be associate to the context, it needs to be associated to the current swapchain.

If the user is using several Renderer with different swapchains like, rendering in different windows, this will break.


The backend API is bad, my fault. It should have been `setPresentationTime(Handle<SwapChain>, int64_t)`.

Code after:
    }
}

void MetalDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
        FrameScheduledCallback&& callback, uint64_t flags) {
    // Turn off the CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER flag if a custom handler is provided.
    if (handler) {
        flags &= ~SwapChain::CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER;
    }
    auto* swapChain = handle_cast<MetalSwapChain>(sch);
    swapChain->setFrameScheduledCallback(handler, std::move(callback), flags);
}

void MetalDriver::setFrameCompletedCallback(
        Handle<HwSwapChain> sch, CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
    auto* swapChain = handle_cast<MetalSwapChain>(sch);
    swapChain->setFrameCompletedCallback(handler, std::move(callback));
}

void MetalDriver::execute(std::function<void(void)> const& fn) noexcept {
    @autoreleasepool {
        fn();
    }
}

void MetalDriver::setPresentationTime(int64_t monotonic_clock_ns) {
    assert_invariant(mContext->currentDrawSwapChain);
    mContext->currentDrawSwapChain->setPresentationTime(monotonic_clock_ns);
}

void MetalDriver::endFrame(uint32_t frameId) {
    DEBUG_LOG("endFrame(frameId = %d)\n", frameId);
    // If we haven't committed the command buffer (if the frame was canceled), do it now. There may
    // be commands in it (like fence signaling) that need to execute.
    submitPendingCommands(mContext);

    mContext->bufferPool->gc();

    // If we acquired a drawable for this frame, ensure that we release it here.
    if (mContext->currentDrawSwapChain) {
        mContext->currentDrawSwapChain->releaseDrawable();
    }

    CVMetalTextureCacheFlush(mContext->textureCache, 0);

    assert_invariant(mContext->groupMarkers.empty());

    // If we exceeded memoryless limits, turn it off for the rest of the lifetime of the driver.
    if (mContext->supportsMemorylessRenderTargets && mContext->memorylessLimitsReached) {
        for (MetalTexture* texture : mContext->textures) {
            // Release memoryless MTLTexture-s, which are currently only the MSAA sidecars.