| /* |
| * 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. |
| */ |
| #include "EmulatedEglFenceSync.h" |
| |
| #include <unordered_set> |
| |
| #include "OpenGLESDispatch/DispatchTables.h" |
| #include "OpenGLESDispatch/EGLDispatch.h" |
| #include "RenderThreadInfoGl.h" |
| #include "StalePtrRegistry.h" |
| #include "aemu/base/containers/Lookup.h" |
| #include "aemu/base/containers/StaticMap.h" |
| #include "aemu/base/files/StreamSerializing.h" |
| #include "aemu/base/synchronization/Lock.h" |
| |
| namespace gfxstream { |
| namespace gl { |
| namespace { |
| |
| using android::base::AutoLock; |
| using android::base::Lock; |
| using android::base::StaticMap; |
| |
| // Timeline class is meant to delete native fences after the |
| // sync device has incremented the timeline. We assume a |
| // maximum number of outstanding timelines in the guest (16) in |
| // order to derive when a native fence is definitely safe to |
| // delete. After at least that many timeline increments have |
| // happened, we sweep away the remaining native fences. |
| // The function that performs the deleting, |
| // incrementTimelineAndDeleteOldFences(), happens on the SyncThread. |
| |
| class Timeline { |
| public: |
| Timeline() = default; |
| |
| static constexpr int kMaxGuestTimelines = 16; |
| void addFence(EmulatedEglFenceSync* fence) { |
| mFences.set(fence, mTime.load() + kMaxGuestTimelines); |
| } |
| |
| void incrementTimelineAndDeleteOldFences() { |
| ++mTime; |
| sweep(); |
| } |
| |
| void sweep() { |
| mFences.eraseIf([time = mTime.load()](EmulatedEglFenceSync* fence, int fenceTime) { |
| EmulatedEglFenceSync* actual = EmulatedEglFenceSync::getFromHandle((uint64_t)(uintptr_t)fence); |
| if (!actual) return true; |
| |
| bool shouldErase = fenceTime <= time; |
| if (shouldErase) { |
| if (!actual->decRef() && |
| actual->shouldDestroyWhenSignaled()) { |
| actual->decRef(); |
| } |
| } |
| return shouldErase; |
| }); |
| } |
| |
| private: |
| std::atomic<int> mTime {0}; |
| StaticMap<EmulatedEglFenceSync*, int> mFences; |
| }; |
| |
| static Timeline* sTimeline() { |
| static Timeline* t = new Timeline; |
| return t; |
| } |
| |
| } // namespace |
| |
| // static |
| void EmulatedEglFenceSync::incrementTimelineAndDeleteOldFences() { |
| sTimeline()->incrementTimelineAndDeleteOldFences(); |
| } |
| |
| // static |
| std::unique_ptr<EmulatedEglFenceSync> EmulatedEglFenceSync::create( |
| EGLDisplay display, |
| bool hasNativeFence, |
| bool destroyWhenSignaled) { |
| auto sync = s_egl.eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr); |
| if (sync == EGL_NO_SYNC_KHR) { |
| ERR("Failed to create EGL fence sync: %d", s_egl.eglGetError()); |
| return nullptr; |
| } |
| |
| // This MUST be present, or we get a deadlock effect. |
| s_gles2.glFlush(); |
| |
| return std::unique_ptr<EmulatedEglFenceSync>( |
| new EmulatedEglFenceSync(display, |
| sync, |
| hasNativeFence, |
| destroyWhenSignaled)); |
| } |
| |
| EmulatedEglFenceSync::EmulatedEglFenceSync(EGLDisplay display, |
| EGLSyncKHR sync, |
| bool hasNativeFence, |
| bool destroyWhenSignaled) |
| : mDestroyWhenSignaled(destroyWhenSignaled), |
| mDisplay(display), |
| mSync(sync) { |
| |
| addToRegistry(); |
| |
| assert(mCount == 1); |
| if (hasNativeFence) { |
| incRef(); |
| sTimeline()->addFence(this); |
| } |
| |
| // Assumes that there is a valid + current OpenGL context |
| assert(RenderThreadInfoGl::get()); |
| } |
| |
| EmulatedEglFenceSync::~EmulatedEglFenceSync() { |
| removeFromRegistry(); |
| } |
| |
| EGLint EmulatedEglFenceSync::wait(uint64_t timeout) { |
| incRef(); |
| EGLint wait_res = |
| s_egl.eglClientWaitSyncKHR(mDisplay, mSync, |
| EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, |
| timeout); |
| decRef(); |
| return wait_res; |
| } |
| |
| void EmulatedEglFenceSync::waitAsync() { |
| s_egl.eglWaitSyncKHR(mDisplay, mSync, 0); |
| } |
| |
| bool EmulatedEglFenceSync::isSignaled() { |
| EGLint val; |
| if (EGL_TRUE == |
| s_egl.eglGetSyncAttribKHR( |
| mDisplay, mSync, EGL_SYNC_STATUS_KHR, &val)) |
| return val == EGL_SIGNALED_KHR; |
| |
| return true; // if invalid, treat as signaled |
| } |
| |
| void EmulatedEglFenceSync::destroy() { |
| s_egl.eglDestroySyncKHR(mDisplay, mSync); |
| } |
| |
| // Snapshots for EmulatedEglFenceSync////////////////////////////////////////////////////// |
| // It's possible, though it does not happen often, that a fence |
| // can be created but not yet waited on by the guest, which |
| // needs careful handling: |
| // |
| // 1. Avoid manipulating garbage memory on snapshot restore; |
| // rcCreateSyncKHR *creates new fence in valid memory* |
| // --snapshot-- |
| // rcClientWaitSyncKHR *refers to uninitialized memory* |
| // rcDestroySyncKHR *refers to uninitialized memory* |
| // 2. Make rcCreateSyncKHR/rcDestroySyncKHR implementations return |
| // the "signaled" status if referring to previous snapshot fences. It's |
| // assumed that the GPU is long done with them. |
| // 3. Avoid name collisions where a new EmulatedEglFenceSync object is created |
| // that has the same uint64_t casting as a EmulatedEglFenceSync object from a previous |
| // snapshot. |
| |
| // Maintain a StalePtrRegistry<EmulatedEglFenceSync>: |
| static StalePtrRegistry<EmulatedEglFenceSync>* sFenceRegistry() { |
| static StalePtrRegistry<EmulatedEglFenceSync>* s = new StalePtrRegistry<EmulatedEglFenceSync>; |
| return s; |
| } |
| |
| // static |
| void EmulatedEglFenceSync::addToRegistry() { |
| sFenceRegistry()->addPtr(this); |
| } |
| |
| // static |
| void EmulatedEglFenceSync::removeFromRegistry() { |
| sFenceRegistry()->removePtr(this); |
| } |
| |
| // static |
| void EmulatedEglFenceSync::onSave(android::base::Stream* stream) { |
| sFenceRegistry()->makeCurrentPtrsStale(); |
| sFenceRegistry()->onSave(stream); |
| } |
| |
| // static |
| void EmulatedEglFenceSync::onLoad(android::base::Stream* stream) { |
| sFenceRegistry()->onLoad(stream); |
| } |
| |
| // static |
| EmulatedEglFenceSync* EmulatedEglFenceSync::getFromHandle(uint64_t handle) { |
| return sFenceRegistry()->getPtr(handle); |
| } |
| |
| } // namespace gl |
| } // namespace gfxstream |