blob: 4b277098fa9ddd8a2f7c4e4d4e835f28c5284153 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "android/base/memory/LazyInstance.h"
#include "android/base/synchronization/Lock.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <atomic>
// The FenceSync class wraps actual EGLSyncKHR objects
// and issues calls to eglCreateSyncKHR, eglClientWaitSyncKHR,
// and eglDestroySyncKHR.
//
// The purpose of this class:
// - We need to track EGL sync objects created by the guest and
// realized in the host OpenGL driver. They are passed between
// guest and host all the time.
// - In particular, we also need to destroy EGL sync objects at
// the proper time. There are at least 3 issues (referenced below
// in spec comments):
// 1 According to spec, we would need to allow concurrent
// eglClientWaitSyncKHR and eglDestroySyncKHR to all finish
// properly.
// 2 If the EGL sync object is of EGL_SYNC_NATIVE_FENCE_ANDROID
// nature, we cannot mirror the guest's call to eglDestroySyncKHR
// by calling the same function on the host, because Goldfish
// sync device can only know when native fence FD's are signaled
// when a host-side EGL sync object is signaled. Thus, we would
// need to delete such sync objects after both the guest and
// the Goldfish sync device are done with them.
// 3 We sometimes create sync objects that are only seen by
// the Goldfish OpenGL driver in the guest, such as for
// implementing eglSwapBuffers() in a way that avoids
// out of order frames. It is cumbersome to eglDestroySyncKHR
// those on the guest, as that would require starting up another guest
// thread and OpenGL context (complete with host connection)
// to destroy it.
class FenceSync {
public:
// The constructor wraps eglCreateSyncKHR on the host OpenGL driver.
// |hasNativeFence| specifies whether this sync object
// is of EGL_SYNC_NATIVE_FENCE_ANDROID nature (2), and
// |destroyWhenSignaled| specifies whether or not to destroy
// the sync object when the native fence FD becomes signaled (3).
FenceSync(bool hasNativeFence,
bool destroyWhenSignaled);
// wait() wraps eglClientWaitSyncKHR. During such a wait, we need
// to increment the reference count while the wait is active,
// in case there is a concurrent call to eglDestroySyncKHR (1).
EGLint wait(uint64_t timeout);
// signaledNativeFd(): upon when the native fence fd
// is signaled due to the sync object being signaled,
// this method does the following:
// - adjusts the reference count down by 1. There is a corresponding
// +1 to the count added at the beginning in the constructor,
// if this fence object is of type EGL_SYNC_NATIVE_FENCE_ANDROID (2).
// - if |mDestroyWhenSignaled| is true, this method decrements the
// reference count again to destroy it (3).
void signaledNativeFd();
// incRef() / decRef() increment/decrement refence counts in order
// to deal with sync object destruction. This is a simple reference
// counting implementation that is almost literally the kref() in
// the Linux kernel.
//
// We do not use shared_ptr or anything like that here because
// we need to explicitly manipulate the reference count in order to
// satisfy (1,2,3) above.
void incRef() { assert(mCount > 0); ++mCount; }
bool decRef() {
assert(mCount > 0);
if (mCount == 1 || --mCount == 0) {
// destroy() here would delay calls to eglDestroySyncKHR
// in the host driver until all waits have completed,
// which is a bit different from simply allowing concurrent calls.
// But, from the guest's perspective, the contract of allowing
// everything to finish is still fulfilled, and there is
// no reason to think (theoretically or practically) that
// is undesirable to destroy the underlying EGL sync object
// a tiny bit later. We could have put in extra logic to allow
// concurrent destruction, but this would have made the code
// undesirably less simple.
destroy();
// This delete-then-return seems OK.
delete this;
return true;
}
return false;
}
private:
bool mDestroyWhenSignaled;
std::atomic<int> mCount = {1};
// EGL state needed for calling OpenGL sync operations.
EGLDisplay mDisplay;
EGLSyncKHR mSync;
// destroy() wraps eglDestroySyncKHR. This is private, because we need
// careful control of when eglDestroySyncKHR is actually called.
void destroy();
};