1 /*
2  * Copyright (C) 2021 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 package android.view.surfacecontrol.cts;
18 
19 import static android.view.cts.util.ASurfaceControlTestUtils.TransactionCompleteListener;
20 import static android.view.cts.util.ASurfaceControlTestUtils.applyAndDeleteSurfaceTransaction;
21 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceControl_createFromWindow;
22 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceControl_release;
23 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_create;
24 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_releaseBuffer;
25 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_setBuffer;
26 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_setEnableBackPressure;
27 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_setOnCompleteCallback;
28 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_setSolidBuffer;
29 import static android.view.cts.util.ASurfaceControlTestUtils.reparent;
30 
31 import static org.junit.Assert.assertTrue;
32 
33 import android.graphics.Canvas;
34 import android.graphics.Color;
35 import android.view.Surface;
36 import android.view.SurfaceHolder;
37 import android.view.cts.surfacevalidator.CapturedActivity;
38 import android.view.cts.surfacevalidator.MultiFramePixelChecker;
39 import android.view.cts.surfacevalidator.SurfaceControlTestCase;
40 
41 import androidx.test.filters.LargeTest;
42 import androidx.test.rule.ActivityTestRule;
43 import androidx.test.runner.AndroidJUnit4;
44 import androidx.test.uiautomator.UiObjectNotFoundException;
45 
46 import org.junit.After;
47 import org.junit.Before;
48 import org.junit.Rule;
49 import org.junit.Test;
50 import org.junit.rules.TestName;
51 import org.junit.runner.RunWith;
52 
53 import java.util.HashSet;
54 import java.util.Set;
55 import java.util.concurrent.CountDownLatch;
56 
57 @LargeTest
58 @RunWith(AndroidJUnit4.class)
59 public class ASurfaceControlBackPressureTest {
60 
61     private static class SyncTransactionCompleteListener implements TransactionCompleteListener {
62         private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
63 
64         @Override
onTransactionComplete(long latchTime, long presentTime)65         public void onTransactionComplete(long latchTime, long presentTime) {
66             mCountDownLatch.countDown();
67         }
68 
waitForTransactionComplete()69         public void waitForTransactionComplete() {
70             try {
71                 mCountDownLatch.await();
72             } catch (InterruptedException e) {
73                 e.printStackTrace();
74             }
75         }
76     }
77 
78     private static final int DEFAULT_LAYOUT_WIDTH = 50;
79     private static final int DEFAULT_LAYOUT_HEIGHT = 50;
80 
81     @Rule
82     public ActivityTestRule<CapturedActivity> mActivityRule =
83             new ActivityTestRule<>(CapturedActivity.class);
84 
85     private CapturedActivity mActivity;
86 
87     @Rule
88     public TestName mName = new TestName();
89 
90     @Before
setup()91     public void setup() {
92         mActivity = mActivityRule.getActivity();
93     }
94 
95     @After
tearDown()96     public void tearDown() throws UiObjectNotFoundException {
97         mActivity.restoreSettings();
98     }
99 
100     public abstract static class BasicSurfaceHolderCallback implements SurfaceHolder.Callback {
101         private final Set<Long> mSurfaceControls = new HashSet<>();
102         private final Set<Long> mBuffers = new HashSet<>();
103         private final Set<BufferCycler> mBufferCyclers = new HashSet<>();
104 
105         // Helper class to submit buffers as fast as possible. The thread submits a buffer,
106         // waits for the transaction complete callback, and then submits the next buffer.
107         class BufferCycler extends Thread {
108             private final long mSurfaceControl;
109             private final long[] mBuffers;
110             private volatile boolean mStop = false;
111             private int mFrameNumber = 0;
112 
BufferCycler(long surfaceControl, long[] buffers)113             BufferCycler(long surfaceControl, long[] buffers) {
114                 mSurfaceControl = surfaceControl;
115                 mBuffers = buffers;
116             }
117 
getNextBuffer()118             private long getNextBuffer() {
119                 return mBuffers[mFrameNumber++ % mBuffers.length];
120             }
121 
122             @Override
run()123             public void run() {
124                 while (!mStop) {
125                     SyncTransactionCompleteListener listener =
126                             new SyncTransactionCompleteListener();
127                     // Send all buffers in batches so we can stuff the SurfaceFlinger transaction
128                     // queue.
129                     for (int i = 0; i < mBuffers.length; i++) {
130                         long surfaceTransaction = createSurfaceTransaction();
131                         nSurfaceTransaction_setBuffer(mSurfaceControl, surfaceTransaction,
132                                 getNextBuffer());
133                         if (i == 0) {
134                             nSurfaceTransaction_setOnCompleteCallback(surfaceTransaction,
135                                     false /* waitForFence */, listener);
136                         }
137                         applyAndDeleteSurfaceTransaction(surfaceTransaction);
138                     }
139 
140                     // Wait for one of transactions to be applied before sending more transactions.
141                     listener.waitForTransactionComplete();
142                 }
143             }
144 
end()145             void end() {
146                 mStop = true;
147             }
148         }
149 
150         @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)151         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
152             Canvas canvas = holder.lockCanvas();
153             canvas.drawColor(Color.YELLOW);
154             holder.unlockCanvasAndPost(canvas);
155         }
156 
157         @Override
surfaceDestroyed(SurfaceHolder holder)158         public void surfaceDestroyed(SurfaceHolder holder) {
159             for (BasicSurfaceHolderCallback.BufferCycler cycler : mBufferCyclers) {
160                 cycler.end();
161                 try {
162                     cycler.join();
163                 } catch (InterruptedException e) {
164                 }
165             }
166             for (Long surfaceControl : mSurfaceControls) {
167                 reparent(surfaceControl, 0);
168                 nSurfaceControl_release(surfaceControl);
169             }
170             mSurfaceControls.clear();
171 
172             for (Long buffer : mBuffers) {
173                 nSurfaceTransaction_releaseBuffer(buffer);
174             }
175             mBuffers.clear();
176         }
177 
createSurfaceTransaction()178         public long createSurfaceTransaction() {
179             long surfaceTransaction = nSurfaceTransaction_create();
180             assertTrue("failed to create surface transaction", surfaceTransaction != 0);
181             return surfaceTransaction;
182         }
183 
createFromWindow(Surface surface)184         public long createFromWindow(Surface surface) {
185             long surfaceControl = nSurfaceControl_createFromWindow(surface);
186             assertTrue("failed to create surface control", surfaceControl != 0);
187 
188             mSurfaceControls.add(surfaceControl);
189             return surfaceControl;
190         }
191 
setEnableBackPressure(long surfaceControl, boolean enableBackPressure)192         public void setEnableBackPressure(long surfaceControl, boolean enableBackPressure) {
193             long surfaceTransaction = createSurfaceTransaction();
194             nSurfaceTransaction_setEnableBackPressure(surfaceControl, surfaceTransaction,
195                     enableBackPressure);
196             applyAndDeleteSurfaceTransaction(surfaceTransaction);
197         }
198 
setSolidBuffer(long surfaceControl, int width, int height, int color)199         public long setSolidBuffer(long surfaceControl, int width, int height, int color) {
200             long surfaceTransaction = createSurfaceTransaction();
201             long buffer = nSurfaceTransaction_setSolidBuffer(
202                     surfaceControl, surfaceTransaction, width, height, color);
203             assertTrue("failed to set buffer", buffer != 0);
204             mBuffers.add(buffer);
205             applyAndDeleteSurfaceTransaction(surfaceTransaction);
206             return buffer;
207         }
208 
addBufferCycler(long surfaceControl, long[] buffers)209         public void addBufferCycler(long surfaceControl, long[] buffers) {
210             BasicSurfaceHolderCallback.BufferCycler cycler =
211                     new BasicSurfaceHolderCallback.BufferCycler(surfaceControl, buffers);
212             cycler.start();
213             mBufferCyclers.add(cycler);
214         }
215     }
216 
217     @Test
testSurfaceTransaction_setEnableBackPressure()218     public void testSurfaceTransaction_setEnableBackPressure() throws Throwable {
219         int[] colors = new int[]{Color.RED, Color.GREEN, Color.BLUE};
220         BasicSurfaceHolderCallback callback = new BasicSurfaceHolderCallback() {
221             @Override
222             public void surfaceCreated(SurfaceHolder holder) {
223                 long surfaceControl = createFromWindow(holder.getSurface());
224                 setEnableBackPressure(surfaceControl, true);
225                 long[] buffers = new long[6];
226                 for (int i = 0; i < buffers.length; i++) {
227                     buffers[i] = setSolidBuffer(surfaceControl, DEFAULT_LAYOUT_WIDTH,
228                             DEFAULT_LAYOUT_HEIGHT, colors[i % colors.length]);
229                 }
230                 addBufferCycler(surfaceControl, buffers);
231             }
232         };
233 
234         MultiFramePixelChecker pixelChecker = new MultiFramePixelChecker(colors) {
235             @Override
236             public boolean checkPixels(int pixelCount, int width, int height) {
237                 return pixelCount > 2000 && pixelCount < 3000;
238             }
239         };
240 
241         mActivity.verifyTest(new SurfaceControlTestCase(callback, pixelChecker,
242                         DEFAULT_LAYOUT_WIDTH, DEFAULT_LAYOUT_HEIGHT,
243                         DEFAULT_LAYOUT_WIDTH, DEFAULT_LAYOUT_HEIGHT),
244                 mName);
245     }
246 
247     @Test
testSurfaceTransaction_defaultBackPressureDisabled()248     public void testSurfaceTransaction_defaultBackPressureDisabled() throws Throwable {
249         int[] colors = new int[]{Color.RED, Color.GREEN, Color.BLUE};
250         BasicSurfaceHolderCallback callback = new BasicSurfaceHolderCallback() {
251             @Override
252             public void surfaceCreated(SurfaceHolder holder) {
253                 long surfaceControl = createFromWindow(holder.getSurface());
254                 // back pressure is disabled by default
255                 long[] buffers = new long[6];
256                 for (int i = 0; i < buffers.length; i++) {
257                     buffers[i] = setSolidBuffer(surfaceControl, DEFAULT_LAYOUT_WIDTH,
258                             DEFAULT_LAYOUT_HEIGHT, colors[i % colors.length]);
259                 }
260                 addBufferCycler(surfaceControl, buffers);
261             }
262         };
263 
264         MultiFramePixelChecker pixelChecker = new MultiFramePixelChecker(colors) {
265             @Override
266             public boolean checkPixels(int pixelCount, int width, int height) {
267                 return pixelCount > 2000 && pixelCount < 3000;
268             }
269         };
270 
271         CapturedActivity.TestResult result = mActivity.runTest(new SurfaceControlTestCase(callback,
272                 pixelChecker,
273                 DEFAULT_LAYOUT_WIDTH, DEFAULT_LAYOUT_HEIGHT,
274                 DEFAULT_LAYOUT_WIDTH, DEFAULT_LAYOUT_HEIGHT));
275 
276         assertTrue(result.passFrames > 0);
277 
278         // With back pressure disabled, the default config, we expect at least one or more frames to
279         // fail since we expect at least one buffer to be dropped.
280         assertTrue(result.failFrames > 0);
281     }
282 }
283