/* * Copyright 2022 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ package org.webrtc; import android.os.Handler; import android.os.HandlerThread; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import org.webrtc.EglBase.EglConnection; /** EGL graphics thread that allows multiple clients to share the same underlying EGLContext. */ public class EglThread { /** Callback for externally managed reference count. */ public interface ReleaseMonitor { /** * Called by EglThread when a client releases its reference. Returns true when there are no more * references and resources should be released. */ boolean onRelease(EglThread eglThread); } public static EglThread create(@Nullable ReleaseMonitor releaseMonitor, @Nullable final EglBase.Context sharedContext, final int[] configAttributes) { final HandlerThread renderThread = new HandlerThread("EglThread"); renderThread.start(); Handler handler = new Handler(renderThread.getLooper()); // Not creating the EGLContext on the thread it will be used on seems to cause issues with // creating window surfaces on certain devices. So keep the same legacy behavior as EglRenderer // and create the context on the render thread. EglConnection eglConnection = ThreadUtils.invokeAtFrontUninterruptibly(handler, () -> { // If sharedContext is null, then texture frames are disabled. This is typically for old // devices that might not be fully spec compliant, so force EGL 1.0 since EGL 1.4 has // caused trouble on some weird devices. if (sharedContext == null) { return EglConnection.createEgl10(configAttributes); } else { return EglConnection.create(sharedContext, configAttributes); } }); return new EglThread( releaseMonitor != null ? releaseMonitor : eglThread -> true, handler, eglConnection); } private final ReleaseMonitor releaseMonitor; private final Handler handler; private final EglConnection eglConnection; @VisibleForTesting EglThread(ReleaseMonitor releaseMonitor, Handler handler, EglConnection eglConnection) { this.releaseMonitor = releaseMonitor; this.handler = handler; this.eglConnection = eglConnection; } public void release() { if (!releaseMonitor.onRelease(this)) { // Thread is still in use, do not release yet. return; } handler.post(eglConnection::release); handler.getLooper().quitSafely(); } /** * Creates an EglBase instance with the EglThread's EglConnection. This method can be called on * any thread, but the returned EglBase instance should only be used on this EglThread's Handler. */ public EglBase createEglBaseWithSharedConnection() { return EglBase.create(eglConnection); } /** * Returns the Handler to interact with Gl/EGL on. Callers need to make sure that their own * EglBase is current on the handler before running any graphics operations since the EglThread * can be shared by multiple clients. */ public Handler getHandler() { return handler; } }