1 /*
2 * Copyright (C) 2018 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 "ReliableSurface.h"
18
19 #include <log/log_main.h>
20 #include <private/android/AHardwareBufferHelpers.h>
21 // TODO: this should be including apex instead.
22 #include <system/window.h>
23 #include <vndk/window.h>
24
25 namespace android::uirenderer::renderthread {
26
27 // TODO: Re-enable after addressing more of the TODO's
28 // With this disabled we won't have a good up-front signal that the surface is no longer valid,
29 // however we can at least handle that reactively post-draw. There's just not a good mechanism
30 // to propagate this error back to the caller
31 constexpr bool DISABLE_BUFFER_PREFETCH = true;
32
ReliableSurface(ANativeWindow * window)33 ReliableSurface::ReliableSurface(ANativeWindow* window) : mWindow(window) {
34 LOG_ALWAYS_FATAL_IF(!mWindow, "Error, unable to wrap a nullptr");
35 ANativeWindow_acquire(mWindow);
36 }
37
~ReliableSurface()38 ReliableSurface::~ReliableSurface() {
39 clearReservedBuffer();
40 // Clear out the interceptors for proper hygiene.
41 // As a concrete example, if the underlying ANativeWindow is associated with
42 // an EGLSurface that is still in use, then if we don't clear out the
43 // interceptors then we walk into undefined behavior.
44 ANativeWindow_setCancelBufferInterceptor(mWindow, nullptr, nullptr);
45 ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr);
46 ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr);
47 ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr);
48 ANativeWindow_setQueryInterceptor(mWindow, nullptr, nullptr);
49 ANativeWindow_release(mWindow);
50 }
51
init()52 void ReliableSurface::init() {
53 int result = ANativeWindow_setCancelBufferInterceptor(mWindow, hook_cancelBuffer, this);
54 LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d",
55 result);
56
57 result = ANativeWindow_setDequeueBufferInterceptor(mWindow, hook_dequeueBuffer, this);
58 LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d",
59 result);
60
61 result = ANativeWindow_setQueueBufferInterceptor(mWindow, hook_queueBuffer, this);
62 LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d",
63 result);
64
65 result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this);
66 LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",
67 result);
68
69 result = ANativeWindow_setQueryInterceptor(mWindow, hook_query, this);
70 LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set query interceptor: error = %d",
71 result);
72 }
73
reserveNext()74 int ReliableSurface::reserveNext() {
75 if constexpr (DISABLE_BUFFER_PREFETCH) {
76 return OK;
77 }
78 {
79 std::lock_guard _lock{mMutex};
80 if (mReservedBuffer) {
81 ALOGW("reserveNext called but there was already a buffer reserved?");
82 return OK;
83 }
84 if (mBufferQueueState != OK) {
85 return UNKNOWN_ERROR;
86 }
87 if (mHasDequeuedBuffer) {
88 return OK;
89 }
90 }
91
92 // TODO: Update this to better handle when requested dimensions have changed
93 // Currently the driver does this via query + perform but that's after we've already
94 // reserved a buffer. Should we do that logic instead? Or should we drop
95 // the backing Surface to the ground and go full manual on the IGraphicBufferProducer instead?
96
97 int fenceFd = -1;
98 ANativeWindowBuffer* buffer = nullptr;
99
100 // Note that this calls back into our own hooked method.
101 int result = ANativeWindow_dequeueBuffer(mWindow, &buffer, &fenceFd);
102
103 {
104 std::lock_guard _lock{mMutex};
105 LOG_ALWAYS_FATAL_IF(mReservedBuffer, "race condition in reserveNext");
106 mReservedBuffer = buffer;
107 mReservedFenceFd.reset(fenceFd);
108 }
109
110 return result;
111 }
112
clearReservedBuffer()113 void ReliableSurface::clearReservedBuffer() {
114 ANativeWindowBuffer* buffer = nullptr;
115 int releaseFd = -1;
116 {
117 std::lock_guard _lock{mMutex};
118 if (mReservedBuffer) {
119 ALOGW("Reserved buffer %p was never used", mReservedBuffer);
120 buffer = mReservedBuffer;
121 releaseFd = mReservedFenceFd.release();
122 }
123 mReservedBuffer = nullptr;
124 mReservedFenceFd.reset();
125 mHasDequeuedBuffer = false;
126 }
127 if (buffer) {
128 // Note that clearReservedBuffer may be reentrant here, so
129 // mReservedBuffer must be cleared once we reach here to avoid recursing
130 // forever.
131 ANativeWindow_cancelBuffer(mWindow, buffer, releaseFd);
132 }
133 }
134
isFallbackBuffer(const ANativeWindowBuffer * windowBuffer) const135 bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const {
136 if (!mScratchBuffer || !windowBuffer) {
137 return false;
138 }
139 ANativeWindowBuffer* scratchBuffer =
140 AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
141 return windowBuffer == scratchBuffer;
142 }
143
acquireFallbackBuffer(int error)144 ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) {
145 std::lock_guard _lock{mMutex};
146 mBufferQueueState = error;
147
148 if (mScratchBuffer) {
149 return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get());
150 }
151
152 AHardwareBuffer_Desc desc;
153 desc.usage = mUsage;
154 desc.format = mFormat;
155 desc.width = 1;
156 desc.height = 1;
157 desc.layers = 1;
158 desc.rfu0 = 0;
159 desc.rfu1 = 0;
160 AHardwareBuffer* newBuffer = nullptr;
161 int err = AHardwareBuffer_allocate(&desc, &newBuffer);
162 if (err) {
163 // Allocate failed, that sucks
164 ALOGW("Failed to allocate scratch buffer, error=%d", err);
165 return nullptr;
166 }
167 mScratchBuffer.reset(newBuffer);
168 return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer);
169 }
170
hook_dequeueBuffer(ANativeWindow * window,ANativeWindow_dequeueBufferFn dequeueBuffer,void * data,ANativeWindowBuffer ** buffer,int * fenceFd)171 int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window,
172 ANativeWindow_dequeueBufferFn dequeueBuffer, void* data,
173 ANativeWindowBuffer** buffer, int* fenceFd) {
174 ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
175 {
176 std::lock_guard _lock{rs->mMutex};
177 if (rs->mReservedBuffer) {
178 *buffer = rs->mReservedBuffer;
179 *fenceFd = rs->mReservedFenceFd.release();
180 rs->mReservedBuffer = nullptr;
181 return OK;
182 }
183 }
184
185 int result = dequeueBuffer(window, buffer, fenceFd);
186 if (result != OK) {
187 ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
188 *buffer = rs->acquireFallbackBuffer(result);
189 *fenceFd = -1;
190 return *buffer ? OK : INVALID_OPERATION;
191 } else {
192 std::lock_guard _lock{rs->mMutex};
193 rs->mHasDequeuedBuffer = true;
194 }
195 return OK;
196 }
197
hook_cancelBuffer(ANativeWindow * window,ANativeWindow_cancelBufferFn cancelBuffer,void * data,ANativeWindowBuffer * buffer,int fenceFd)198 int ReliableSurface::hook_cancelBuffer(ANativeWindow* window,
199 ANativeWindow_cancelBufferFn cancelBuffer, void* data,
200 ANativeWindowBuffer* buffer, int fenceFd) {
201 ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
202 rs->clearReservedBuffer();
203 if (rs->isFallbackBuffer(buffer)) {
204 if (fenceFd > 0) {
205 close(fenceFd);
206 }
207 return OK;
208 }
209 return cancelBuffer(window, buffer, fenceFd);
210 }
211
hook_queueBuffer(ANativeWindow * window,ANativeWindow_queueBufferFn queueBuffer,void * data,ANativeWindowBuffer * buffer,int fenceFd)212 int ReliableSurface::hook_queueBuffer(ANativeWindow* window,
213 ANativeWindow_queueBufferFn queueBuffer, void* data,
214 ANativeWindowBuffer* buffer, int fenceFd) {
215 ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
216 rs->clearReservedBuffer();
217
218 if (rs->isFallbackBuffer(buffer)) {
219 if (fenceFd > 0) {
220 close(fenceFd);
221 }
222 return OK;
223 }
224
225 return queueBuffer(window, buffer, fenceFd);
226 }
227
hook_perform(ANativeWindow * window,ANativeWindow_performFn perform,void * data,int operation,va_list args)228 int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn perform,
229 void* data, int operation, va_list args) {
230 // Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions
231 // TODO: Filter to things that only affect the reserved buffer
232 // TODO: Can we mutate the reserved buffer in some cases?
233 ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
234 rs->clearReservedBuffer();
235
236 va_list argsCopy;
237 va_copy(argsCopy, args);
238 int result = perform(window, operation, argsCopy);
239
240 {
241 std::lock_guard _lock{rs->mMutex};
242
243 switch (operation) {
244 case ANATIVEWINDOW_PERFORM_SET_USAGE:
245 rs->mUsage = va_arg(args, uint32_t);
246 break;
247 case ANATIVEWINDOW_PERFORM_SET_USAGE64:
248 rs->mUsage = va_arg(args, uint64_t);
249 break;
250 case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY:
251 /* width */ va_arg(args, uint32_t);
252 /* height */ va_arg(args, uint32_t);
253 rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t));
254 break;
255 case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT:
256 rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t));
257 break;
258 case NATIVE_WINDOW_SET_BUFFER_COUNT:
259 size_t bufferCount = va_arg(args, size_t);
260 if (bufferCount >= rs->mExpectedBufferCount) {
261 rs->mDidSetExtraBuffers = true;
262 } else {
263 ALOGD("HOOK FAILED! Expected %zd got = %zd", rs->mExpectedBufferCount, bufferCount);
264 }
265 break;
266 }
267 }
268 return result;
269 }
270
hook_query(const ANativeWindow * window,ANativeWindow_queryFn query,void * data,int what,int * value)271 int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_queryFn query,
272 void *data, int what, int *value) {
273 ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
274 int result = query(window, what, value);
275 if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) {
276 std::lock_guard _lock{rs->mMutex};
277 *value += rs->mExtraBuffers;
278 rs->mExpectedBufferCount = *value + 2;
279 }
280 return result;
281 }
282
283 }; // namespace android::uirenderer::renderthread
284