OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 package org.chromium.chrome.browser.compositor; | |
6 | |
7 import android.content.Context; | |
8 import android.graphics.PixelFormat; | |
9 import android.graphics.drawable.Drawable; | |
10 import android.os.Build; | |
11 import android.view.SurfaceHolder; | |
12 import android.view.SurfaceView; | |
13 import android.view.View; | |
14 import android.view.ViewGroup; | |
15 import android.widget.FrameLayout; | |
16 | |
17 /** | |
18 * Manage multiple SurfaceViews for the compositor, so that transitions between | |
aelias_OOO_until_Jul13
2017/01/12 21:46:05
Could you copy your design doc https://docs.google
liberato (no reviews please)
2017/01/19 18:12:34
Done, and in this comment.
| |
19 * surfaces with and without an alpha channel can be visually smooth. | |
20 * | |
21 * This class allows a client to request a 'translucent' or 'opaque' surface, an d we will signal via | |
22 * SurfaceHolder.Callback when it's ready. We guarantee that the client will re ceive surfaceCreated | |
23 * / surfaceDestroyed only for a surface that represents the most recently reque sted PixelFormat. | |
24 * | |
25 * Internally, we maintain two SurfaceViews, since calling setFormat() to change the PixelFormat | |
26 * results in a visual glitch as the surface is torn down. crbug.com/679902 | |
27 * | |
28 * The client has the responsibility to call doneWithUnownedSurface() at some po int between when we | |
29 * call back its surfaceCreated, when it is safe for us to hide the SurfaceView with the wrong | |
30 * format. | |
31 * | |
32 * We do not guarantee that the surface will always actually have that format, t hough, if we're set | |
33 * up to always provide a translucent surface. This helps low-memory devices, b ut not requiring two | |
34 * SurfaceViews. However, it does require that the compositor use the EGL confi g to turn off alpha | |
35 * blending. For low memory devices, this already happens, since it selects bet ween 565 and 8888 | |
36 * EGL configs. In this case, we will still provide synthetic surfaceCreated / surfaceDestroyed | |
37 * messages to the client, even though the underlying surface isn't changing. | |
38 */ | |
39 class CompositorSurfaceManager implements SurfaceHolder.Callback { | |
40 private static class SurfaceState { | |
41 public SurfaceView surfaceView; | |
42 | |
43 // Have we started destroying |surfaceView|, but haven't been notified o f it yet? | |
44 public boolean destroyPending; | |
45 | |
46 public SurfaceState(Context context, int format, SurfaceHolder.Callback callback) { | |
47 surfaceView = new SurfaceView(context); | |
48 surfaceView.setZOrderMediaOverlay(true); | |
49 surfaceView.setVisibility(View.VISIBLE); | |
50 surfaceHolder().setFormat(format); | |
51 surfaceHolder().addCallback(callback); | |
52 } | |
53 | |
54 public SurfaceHolder surfaceHolder() { | |
55 return surfaceView.getHolder(); | |
56 } | |
57 | |
58 public boolean isValid() { | |
59 return surfaceHolder().getSurface().isValid(); | |
60 } | |
61 } | |
62 | |
63 // SurfaceView with a translucent PixelFormat. | |
64 private SurfaceState mTranslucent; | |
65 | |
66 // SurfaceView with an opaque PixelFormat. | |
67 private SurfaceState mOpaque; | |
68 | |
69 // Surface that we last gave to the client with surfaceCreated. Cleared whe n we call | |
70 // surfaceDestroyed. | |
71 private SurfaceState mOwnedByClient; | |
72 | |
73 // Surface that was most recently requested by the client. | |
74 private SurfaceState mRequestedByClient; | |
75 | |
76 // Client that we notify about surface change events. | |
77 private SurfaceHolder.Callback mClient; | |
78 | |
79 // View to which we'll attach the SurfaceView. | |
80 private final ViewGroup mParentView; | |
81 | |
82 public CompositorSurfaceManager(ViewGroup parentView, SurfaceHolder.Callback client) { | |
83 mParentView = parentView; | |
84 mClient = client; | |
85 | |
86 mTranslucent = new SurfaceState(parentView.getContext(), PixelFormat.TRA NSLUCENT, this); | |
87 mOpaque = new SurfaceState(mParentView.getContext(), PixelFormat.OPAQUE, this); | |
88 } | |
89 | |
90 /** | |
91 * Turn off everything. | |
92 */ | |
93 public void shutDown() { | |
94 mTranslucent.surfaceHolder().removeCallback(this); | |
95 if (mOpaque != null) mOpaque.surfaceHolder().removeCallback(this); | |
96 } | |
97 | |
98 /** | |
99 * Called by the client to request a surface. Once called, we guarantee tha t the next call to | |
100 * onSurfaceAvailable will match the most recent value of |format|. If the surface is already | |
101 * available for use, then we'll elide the created callback. Note that |for mat| must be either | |
102 * OPAQUE or TRANSLUCENT. | |
103 */ | |
104 public void requestSurface(int format) { | |
105 mRequestedByClient = (format == PixelFormat.TRANSLUCENT) ? mTranslucent : mOpaque; | |
106 | |
107 // Is the requested surface ready? "Ready" means that it's valid, and t hat we don't have a | |
108 // destroy in-flight for it. | |
109 if (mRequestedByClient.isValid() && !mRequestedByClient.destroyPending) { | |
110 // The client has requested a surface that's already valid and isn't being torn down. | |
111 // It might be requesting the same surface it already has, or it mig ht have toggled | |
112 // back to one it had before without notifying doneWithUnownedSurfac e. In either case, | |
113 // we elide the surface created / destroyed callbacks. We might wan t to return whether | |
114 // we've elided them, but CompositorView doesn't care. | |
115 mOwnedByClient = mRequestedByClient; | |
116 return; | |
117 } | |
118 | |
119 // Surface is not valid, or it'll be destroyed soon. If it's not valid now, then start | |
120 // construction. Once it's ready, we'll signal the client if it still w ants this surface. | |
121 // If destruction is pending, then we must wait for it to complete. Whe n we're notified | |
122 // that it is destroyed, we'll re-start construction if the client still wants this surface. | |
123 // Note that we could send a surfaceDestroyed for the owned surface, if there is one, but we | |
124 // defer it until later so that the compositor can still use it. | |
125 if (!mRequestedByClient.isValid()) startSurfaceConstruction(mRequestedBy Client); | |
126 } | |
127 | |
128 /** | |
129 * Called to notify us that the client no longer needs the surface that it d oesn't own. This | |
130 * tells us that we may destroy it. Note that it's okay if it never had an unowned surface. | |
131 */ | |
132 public void doneWithUnownedSurface() { | |
133 if (mOwnedByClient == null) return; | |
134 | |
135 SurfaceState unowned = (mOwnedByClient == mTranslucent) ? mOpaque : mTra nslucent; | |
136 | |
137 if (mRequestedByClient == unowned) { | |
138 // Client is giving us back a surface that it's since requested but hasn't gotten yet. | |
139 // Do nothing. It will be notified when the new surface is ready, a nd it can call us | |
140 // again. | |
141 return; | |
142 } | |
143 | |
144 // Start destruction of this surface. | |
145 postSurfaceDestruction(unowned); | |
146 } | |
147 | |
148 /** | |
149 * Return the currently owned SurfaceHolder, if any. | |
150 */ | |
151 public SurfaceHolder getHolder() { | |
152 return mOwnedByClient != null ? mOwnedByClient.surfaceHolder() : null; | |
153 } | |
154 | |
155 /** | |
156 * Destroy and re-create the surface. Useful for a JB workaround needed by CompositorView. | |
157 */ | |
158 public void recreateSurfaceForJellyBean() { | |
159 assert Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2; | |
160 | |
161 // If they don't have a surface, then they'll get a new one anyway. | |
162 if (mOwnedByClient == null) return; | |
163 | |
164 // Notify the client that it no longer owns this surface, then destroy i t. When destruction | |
165 // completes, we will recreate it automatically, since it will look like the client since | |
166 // re-requested it. That's why we send surfaceDestroyed here rather tha n letting our | |
167 // surfaceDestroyed do it when destruction completes. If we just starte d destruction while | |
168 // the client still owns the surface, then our surfaceDestroyed would as sume that android | |
169 // initiated the destruction since |mSurfaceDestroyed| would imply that the client didn't. | |
170 | |
171 mParentView.post(new Runnable() { | |
172 @Override | |
173 public void run() { | |
174 if (mOwnedByClient == null) return; | |
175 SurfaceState owned = mOwnedByClient; | |
176 mClient.surfaceDestroyed(mOwnedByClient.surfaceHolder()); | |
177 mOwnedByClient = null; | |
178 startSurfaceDestruction(owned); | |
179 } | |
180 }); | |
181 } | |
182 | |
183 @Override | |
184 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { | |
185 SurfaceState state = getStateForHolder(holder); | |
186 assert state != null; | |
187 | |
188 // If this is the surface that the client currently cares about, then no tify the client. | |
189 // Note that surfaceChanged is guaranteed to come only after surfaceCrea ted. Also, if the | |
190 // client has requested a different surface but hasn't gotten it yet, th en skip this. | |
191 if (state == mOwnedByClient && state == mRequestedByClient) { | |
192 mClient.surfaceChanged(holder, format, width, height); | |
193 } | |
194 } | |
195 | |
196 @Override | |
197 public void surfaceCreated(SurfaceHolder holder) { | |
198 SurfaceState state = getStateForHolder(holder); | |
199 assert state != null; | |
200 | |
201 if (state != mRequestedByClient) { | |
202 // Surface is created, but it's not the one that's been requested mo st recently. Just | |
203 // destroy it again. | |
204 postSurfaceDestruction(state); | |
205 return; | |
206 } | |
207 | |
208 // The client requested a surface, and it's now available. If the clien t owns a | |
209 // surface, then notify it that it doesn't. Note that the client can't own |state| at | |
210 // this point, since we would have removed ownership when we got surface Destroyed. | |
211 if (mOwnedByClient != null) mClient.surfaceDestroyed(mOwnedByClient.surf aceHolder()); | |
212 | |
213 mOwnedByClient = mRequestedByClient; | |
214 mClient.surfaceCreated(mOwnedByClient.surfaceHolder()); | |
215 } | |
216 | |
217 @Override | |
218 public void surfaceDestroyed(SurfaceHolder holder) { | |
219 SurfaceState state = getStateForHolder(holder); | |
220 assert state != null; | |
221 | |
222 // We might not have requested destruction, but clear the flag in case w e did. | |
223 state.destroyPending = false; | |
224 | |
225 // If the client owns this surface, then notify it synchronously that it no longer does. | |
226 // This can happen if Android destroys the surface on its own. | |
227 if (state == mOwnedByClient) { | |
228 mClient.surfaceDestroyed(holder); | |
229 mOwnedByClient = null; | |
230 | |
231 // Do not re-request the surface here. If android gives the surface back, then we'll | |
232 // re-signal the client about construction. | |
233 return; | |
234 } | |
235 | |
236 // The client doesn't own this surface, but might want it. | |
237 // If the client has requested this surface, then start construction on it. The client will | |
238 // be notified when it completes. This can happen if the client re-requ ests a surface after | |
239 // we start destruction on it from a previous request, for example. We post this for later, | |
240 // since we might be called while removing |state| from the view tree. I n general, posting | |
241 // from here is good. | |
242 if (state == mRequestedByClient) { | |
243 postSurfaceConstruction(state); | |
244 } else { | |
245 // This isn't the requested surface. If android destroyed it, then also unhook it so | |
246 // that it isn't recreated later. If we did, then it's already not attached, and this | |
247 // will do nothing. | |
248 postSurfaceDestruction(state); | |
249 } | |
250 } | |
251 | |
252 /** | |
253 * Update the background drawable on all surfaces. | |
254 */ | |
255 public void setBackgroundDrawable(Drawable background) { | |
256 mTranslucent.surfaceView.setBackgroundDrawable(background); | |
257 if (mOpaque != null) mOpaque.surfaceView.setBackgroundDrawable(backgroun d); | |
258 } | |
259 | |
260 /** | |
261 * Set |willNotDraw| on all surfaces. | |
262 */ | |
263 public void setWillNotDraw(boolean willNotDraw) { | |
264 mTranslucent.surfaceView.setWillNotDraw(willNotDraw); | |
265 if (mOpaque != null) mOpaque.surfaceView.setWillNotDraw(willNotDraw); | |
266 } | |
267 | |
268 /** | |
269 * Return the SurfaceState for |holder|, or null if it isn't either. | |
270 */ | |
271 private SurfaceState getStateForHolder(SurfaceHolder holder) { | |
272 if (mTranslucent.surfaceHolder() == holder) return mTranslucent; | |
273 | |
274 if (mOpaque != null && mOpaque.surfaceHolder() == holder) return mOpaque ; | |
275 | |
276 return null; | |
277 } | |
278 | |
279 private void startSurfaceConstruction(SurfaceState state) { | |
280 if (state.surfaceView.getParent() != null) return; | |
281 | |
282 FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( | |
283 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATC H_PARENT); | |
284 mParentView.addView(state.surfaceView, lp); | |
285 mParentView.bringChildToFront(state.surfaceView); | |
286 mParentView.postInvalidateOnAnimation(); | |
287 } | |
288 | |
289 private void postSurfaceConstruction(final SurfaceState state) { | |
290 mParentView.post(new Runnable() { | |
291 @Override | |
292 public void run() { | |
293 startSurfaceConstruction(state); | |
294 } | |
295 }); | |
296 } | |
297 | |
298 private void startSurfaceDestruction(SurfaceState state) { | |
299 // If we're called while we're not attached, then do nothing. This make s it easier for the | |
300 // client, since it doesn't have to keep track of whether the outgoing s urface has been | |
301 // destroyed or not. | |
302 if (state.surfaceView.getParent() == null) return; | |
303 | |
304 state.destroyPending = true; | |
305 mParentView.removeView(state.surfaceView); | |
306 } | |
307 | |
308 private void postSurfaceDestruction(final SurfaceState state) { | |
309 mParentView.post(new Runnable() { | |
310 @Override | |
311 public void run() { | |
312 startSurfaceDestruction(state); | |
313 } | |
314 }); | |
315 } | |
316 } | |
OLD | NEW |