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