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