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