blob: 4cccd0ff1c3624533139392df70d3c9732da71d1 [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 <atomic>
#include <memory>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "aemu/base/Compiler.h"
#include "aemu/base/files/Stream.h"
#include "aemu/base/synchronization/Lock.h"
namespace gfxstream {
namespace gl {
// The EmulatedEglFenceSync 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 EmulatedEglFenceSync {
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).
static std::unique_ptr<EmulatedEglFenceSync> create(
EGLSyncKHR sync,
bool hasNativeFence,
bool destroyWhenSignaled);
~EmulatedEglFenceSync();
// 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);
// waitAsync wraps eglWaitSyncKHR.
void waitAsync();
// isSignaled wraps eglGetSyncAttribKHR.
bool isSignaled();
bool shouldDestroyWhenSignaled() const {
return mDestroyWhenSignaled;
}
// When a native fence gets signaled, this function is called to update the
// timeline counter in the EmulatedEglFenceSync internal timeline and delete old
// fences.
static void incrementTimelineAndDeleteOldFences();
// 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;
}
void setIsCompositionFence(bool isComposition) {
mIsCompositionFence = isComposition;
}
bool isCompositionFence() const {
return mIsCompositionFence;
}
// Tracks current active set of fences. Useful for snapshotting.
void addToRegistry();
void removeFromRegistry();
static EmulatedEglFenceSync* getFromHandle(uint64_t handle);
// Functions for snapshotting all fence state at once
static void onSave(android::base::Stream* stream);
static void onLoad(android::base::Stream* stream);
private:
EmulatedEglFenceSync(EGLDisplay display,
EGLSyncKHR sync,
bool hasNativeFence,
bool destroyWhenSignaled);
bool mDestroyWhenSignaled;
std::atomic<int> mCount {1};
// EGL state needed for calling OpenGL sync operations.
EGLDisplay mDisplay;
EGLSyncKHR mSync;
// Whether this fence was against composition, in which case
// we should make this wait till next vsync.
bool mIsCompositionFence = false;
// destroy() wraps eglDestroySyncKHR. This is private, because we need
// careful control of when eglDestroySyncKHR is actually called.
void destroy();
DISALLOW_COPY_AND_ASSIGN(EmulatedEglFenceSync);
};
} // namespace gl
} // namespace gfxstream