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