1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 
17 #include "EmulatedEglFenceSync.h"
18 
19 #include <unordered_set>
20 
21 #include "OpenGLESDispatch/DispatchTables.h"
22 #include "OpenGLESDispatch/EGLDispatch.h"
23 #include "RenderThreadInfoGl.h"
24 #include "StalePtrRegistry.h"
25 #include "aemu/base/containers/Lookup.h"
26 #include "aemu/base/containers/StaticMap.h"
27 #include "aemu/base/files/StreamSerializing.h"
28 #include "aemu/base/synchronization/Lock.h"
29 
30 namespace gfxstream {
31 namespace gl {
32 namespace {
33 
34 using android::base::AutoLock;
35 using android::base::Lock;
36 using android::base::StaticMap;
37 
38 // Timeline class is meant to delete native fences after the
39 // sync device has incremented the timeline.  We assume a
40 // maximum number of outstanding timelines in the guest (16) in
41 // order to derive when a native fence is definitely safe to
42 // delete. After at least that many timeline increments have
43 // happened, we sweep away the remaining native fences.
44 // The function that performs the deleting,
45 // incrementTimelineAndDeleteOldFences(), happens on the SyncThread.
46 
47 class Timeline {
48   public:
49     Timeline() = default;
50 
51     static constexpr int kMaxGuestTimelines = 16;
addFence(EmulatedEglFenceSync * fence)52     void addFence(EmulatedEglFenceSync* fence) {
53         mFences.set(fence, mTime.load() + kMaxGuestTimelines);
54     }
55 
incrementTimelineAndDeleteOldFences()56     void incrementTimelineAndDeleteOldFences() {
57         ++mTime;
58         sweep();
59     }
60 
sweep()61     void sweep() {
62         mFences.eraseIf([time = mTime.load()](EmulatedEglFenceSync* fence, int fenceTime) {
63             EmulatedEglFenceSync* actual = EmulatedEglFenceSync::getFromHandle((uint64_t)(uintptr_t)fence);
64             if (!actual) return true;
65 
66             bool shouldErase = fenceTime <= time;
67             if (shouldErase) {
68                 if (!actual->decRef() &&
69                     actual->shouldDestroyWhenSignaled()) {
70                     actual->decRef();
71                 }
72             }
73             return shouldErase;
74         });
75     }
76 
77   private:
78     std::atomic<int> mTime {0};
79     StaticMap<EmulatedEglFenceSync*, int> mFences;
80 };
81 
sTimeline()82 static Timeline* sTimeline() {
83     static Timeline* t = new Timeline;
84     return t;
85 }
86 
87 }  // namespace
88 
89 // static
incrementTimelineAndDeleteOldFences()90 void EmulatedEglFenceSync::incrementTimelineAndDeleteOldFences() {
91     sTimeline()->incrementTimelineAndDeleteOldFences();
92 }
93 
94 // static
create(EGLDisplay display,bool hasNativeFence,bool destroyWhenSignaled)95 std::unique_ptr<EmulatedEglFenceSync> EmulatedEglFenceSync::create(
96         EGLDisplay display,
97         bool hasNativeFence,
98         bool destroyWhenSignaled) {
99     auto sync = s_egl.eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, nullptr);
100     if (sync == EGL_NO_SYNC_KHR) {
101         ERR("Failed to create EGL fence sync: %d", s_egl.eglGetError());
102         return nullptr;
103     }
104 
105     // This MUST be present, or we get a deadlock effect.
106     s_gles2.glFlush();
107 
108     return std::unique_ptr<EmulatedEglFenceSync>(
109         new EmulatedEglFenceSync(display,
110                                  sync,
111                                  hasNativeFence,
112                                  destroyWhenSignaled));
113 }
114 
EmulatedEglFenceSync(EGLDisplay display,EGLSyncKHR sync,bool hasNativeFence,bool destroyWhenSignaled)115 EmulatedEglFenceSync::EmulatedEglFenceSync(EGLDisplay display,
116                                            EGLSyncKHR sync,
117                                            bool hasNativeFence,
118                                            bool destroyWhenSignaled)
119     : mDestroyWhenSignaled(destroyWhenSignaled),
120       mDisplay(display),
121       mSync(sync) {
122 
123     addToRegistry();
124 
125     assert(mCount == 1);
126     if (hasNativeFence) {
127         incRef();
128         sTimeline()->addFence(this);
129     }
130 
131     // Assumes that there is a valid + current OpenGL context
132     assert(RenderThreadInfoGl::get());
133 }
134 
~EmulatedEglFenceSync()135 EmulatedEglFenceSync::~EmulatedEglFenceSync() {
136     removeFromRegistry();
137 }
138 
wait(uint64_t timeout)139 EGLint EmulatedEglFenceSync::wait(uint64_t timeout) {
140     incRef();
141     EGLint wait_res =
142         s_egl.eglClientWaitSyncKHR(mDisplay, mSync,
143                                    EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
144                                    timeout);
145     decRef();
146     return wait_res;
147 }
148 
waitAsync()149 void EmulatedEglFenceSync::waitAsync() {
150     s_egl.eglWaitSyncKHR(mDisplay, mSync, 0);
151 }
152 
isSignaled()153 bool EmulatedEglFenceSync::isSignaled() {
154     EGLint val;
155     if (EGL_TRUE ==
156             s_egl.eglGetSyncAttribKHR(
157                 mDisplay, mSync, EGL_SYNC_STATUS_KHR, &val))
158         return val == EGL_SIGNALED_KHR;
159 
160     return true; // if invalid, treat as signaled
161 }
162 
destroy()163 void EmulatedEglFenceSync::destroy() {
164     s_egl.eglDestroySyncKHR(mDisplay, mSync);
165 }
166 
167 // Snapshots for EmulatedEglFenceSync//////////////////////////////////////////////////////
168 // It's possible, though it does not happen often, that a fence
169 // can be created but not yet waited on by the guest, which
170 // needs careful handling:
171 //
172 // 1. Avoid manipulating garbage memory on snapshot restore;
173 // rcCreateSyncKHR *creates new fence in valid memory*
174 // --snapshot--
175 // rcClientWaitSyncKHR *refers to uninitialized memory*
176 // rcDestroySyncKHR *refers to uninitialized memory*
177 // 2. Make rcCreateSyncKHR/rcDestroySyncKHR implementations return
178 // the "signaled" status if referring to previous snapshot fences. It's
179 // assumed that the GPU is long done with them.
180 // 3. Avoid name collisions where a new EmulatedEglFenceSync object is created
181 // that has the same uint64_t casting as a EmulatedEglFenceSync object from a previous
182 // snapshot.
183 
184 // Maintain a StalePtrRegistry<EmulatedEglFenceSync>:
sFenceRegistry()185 static StalePtrRegistry<EmulatedEglFenceSync>* sFenceRegistry() {
186     static StalePtrRegistry<EmulatedEglFenceSync>* s = new StalePtrRegistry<EmulatedEglFenceSync>;
187     return s;
188 }
189 
190 // static
addToRegistry()191 void EmulatedEglFenceSync::addToRegistry() {
192     sFenceRegistry()->addPtr(this);
193 }
194 
195 // static
removeFromRegistry()196 void EmulatedEglFenceSync::removeFromRegistry() {
197     sFenceRegistry()->removePtr(this);
198 }
199 
200 // static
onSave(android::base::Stream * stream)201 void EmulatedEglFenceSync::onSave(android::base::Stream* stream) {
202     sFenceRegistry()->makeCurrentPtrsStale();
203     sFenceRegistry()->onSave(stream);
204 }
205 
206 // static
onLoad(android::base::Stream * stream)207 void EmulatedEglFenceSync::onLoad(android::base::Stream* stream) {
208     sFenceRegistry()->onLoad(stream);
209 }
210 
211 // static
getFromHandle(uint64_t handle)212 EmulatedEglFenceSync* EmulatedEglFenceSync::getFromHandle(uint64_t handle) {
213     return sFenceRegistry()->getPtr(handle);
214 }
215 
216 }  // namespace gl
217 }  // namespace gfxstream
218