Merge "Add more tests to verify blast buffer queue behaviour"

This commit is contained in:
TreeHugger Robot
2020-11-19 19:54:57 +00:00
committed by Android (Google) Code Review
16 changed files with 1104 additions and 203 deletions

View File

@@ -33,6 +33,8 @@ android_test {
"kotlinx-coroutines-android",
"flickerlib",
"truth-prebuilt",
"cts-wm-util",
"CtsSurfaceValidatorLib",
],
}
@@ -43,6 +45,7 @@ cc_library_shared {
],
shared_libs: [
"libutils",
"libui",
"libgui",
"liblog",
"libandroid",

View File

@@ -23,12 +23,19 @@
<uses-permission android:name="android.permission.DUMP" />
<!-- Enable / Disable sv blast adapter !-->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<!-- Readback virtual display output !-->
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<!-- Save failed test bitmap images !-->
<uses-permission android:name="android.Manifest.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:allowBackup="false"
android:supportsRtl="true">
<activity android:name=".MainActivity"
android:taskAffinity="com.android.test.MainActivity"
android:theme="@style/AppTheme"
android:configChanges="orientation|screenSize"
android:label="SurfaceViewBufferTestApp"
android:exported="true">
<intent-filter>
@@ -36,6 +43,10 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:enabled="true">
</service>
<uses-library android:name="android.test.runner"/>
</application>

View File

@@ -32,6 +32,7 @@
extern "C" {
int i = 0;
static ANativeWindow* sAnw;
static std::map<uint32_t /* slot */, ANativeWindowBuffer*> sBuffers;
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env, jclass,
jobject surfaceObject) {
@@ -39,11 +40,14 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_setSurface(JNIEnv* env
assert(sAnw);
android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
surface->enableFrameTimestamps(true);
surface->connect(NATIVE_WINDOW_API_CPU, nullptr, false);
native_window_set_usage(sAnw, GRALLOC_USAGE_SW_WRITE_OFTEN);
native_window_set_buffers_format(sAnw, HAL_PIXEL_FORMAT_RGBA_8888);
return 0;
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplayed(
JNIEnv*, jclass, jint jFrameNumber, jint timeoutSec) {
JNIEnv*, jclass, jlong jFrameNumber, jint timeoutMs) {
using namespace std::chrono_literals;
assert(sAnw);
android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
@@ -63,8 +67,8 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_waitUntilBufferDisplay
&outDisplayPresentTime, &outDequeueReadyTime, &outReleaseTime);
if (outDisplayPresentTime < 0) {
auto end = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(end - start).count() >
timeoutSec) {
if (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() >
timeoutMs) {
return -1;
}
}
@@ -99,4 +103,121 @@ JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffer
assert(sAnw);
return ANativeWindow_setBuffersGeometry(sAnw, w, h, format);
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_ANativeWindowSetBuffersTransform(
JNIEnv* /* env */, jclass /* clazz */, jint transform) {
assert(sAnw);
return native_window_set_buffers_transform(sAnw, transform);
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceSetScalingMode(JNIEnv* /* env */,
jclass /* clazz */,
jint scalingMode) {
assert(sAnw);
android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
return surface->setScalingMode(scalingMode);
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceDequeueBuffer(JNIEnv* /* env */,
jclass /* clazz */,
jint slot,
jint timeoutMs) {
assert(sAnw);
ANativeWindowBuffer* anb;
int fenceFd;
int result = sAnw->dequeueBuffer(sAnw, &anb, &fenceFd);
if (result != android::OK) {
return result;
}
sBuffers[slot] = anb;
android::sp<android::Fence> fence(new android::Fence(fenceFd));
int waitResult = fence->wait(timeoutMs);
if (waitResult != android::OK) {
sAnw->cancelBuffer(sAnw, sBuffers[slot], -1);
sBuffers[slot] = nullptr;
return waitResult;
}
return 0;
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceCancelBuffer(JNIEnv* /* env */,
jclass /* clazz */,
jint slot) {
assert(sAnw);
assert(sBuffers[slot]);
int result = sAnw->cancelBuffer(sAnw, sBuffers[slot], -1);
sBuffers[slot] = nullptr;
return result;
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_drawBuffer(JNIEnv* env,
jclass /* clazz */, jint slot,
jintArray jintArrayColor) {
assert(sAnw);
assert(sBuffers[slot]);
int* color = env->GetIntArrayElements(jintArrayColor, nullptr);
ANativeWindowBuffer* buffer = sBuffers[slot];
android::sp<android::GraphicBuffer> graphicBuffer(static_cast<android::GraphicBuffer*>(buffer));
const android::Rect bounds(buffer->width, buffer->height);
android::Region newDirtyRegion;
newDirtyRegion.set(bounds);
void* vaddr;
int fenceFd = -1;
graphicBuffer->lockAsync(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
newDirtyRegion.bounds(), &vaddr, fenceFd);
for (int32_t row = 0; row < buffer->height; row++) {
uint8_t* dst = static_cast<uint8_t*>(vaddr) + (buffer->stride * row) * 4;
for (int32_t column = 0; column < buffer->width; column++) {
dst[0] = color[0];
dst[1] = color[1];
dst[2] = color[2];
dst[3] = color[3];
dst += 4;
}
}
graphicBuffer->unlockAsync(&fenceFd);
env->ReleaseIntArrayElements(jintArrayColor, color, JNI_ABORT);
return 0;
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_SurfaceQueueBuffer(JNIEnv* /* env */,
jclass /* clazz */,
jint slot,
jboolean freeSlot) {
assert(sAnw);
assert(sBuffers[slot]);
int result = sAnw->queueBuffer(sAnw, sBuffers[slot], -1);
if (freeSlot) {
sBuffers[slot] = nullptr;
}
return result;
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetBufferCount(
JNIEnv* /* env */, jclass /* clazz */, jint count) {
assert(sAnw);
android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
int result = native_window_set_buffer_count(sAnw, count);
return result;
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetSharedBufferMode(
JNIEnv* /* env */, jclass /* clazz */, jboolean shared) {
assert(sAnw);
android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
int result = native_window_set_shared_buffer_mode(sAnw, shared);
return result;
}
JNIEXPORT jint JNICALL Java_com_android_test_SurfaceProxy_NativeWindowSetAutoRefresh(
JNIEnv* /* env */, jclass /* clazz */, jboolean autoRefresh) {
assert(sAnw);
android::sp<android::Surface> surface = static_cast<android::Surface*>(sAnw);
int result = native_window_set_auto_refresh(sAnw, autoRefresh);
return result;
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
/** Submit buffers as fast as possible and make sure they are presented on display */
@Test
fun testQueueBuffers() {
val numFrames = 100L
val trace = withTrace {
for (i in 1..numFrames) {
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
}
assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 1000 /* ms */))
}
assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
@Test
fun testSetBufferScalingMode_outOfOrderQueueBuffer() {
val trace = withTrace {
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
it.mSurfaceProxy.SurfaceQueueBuffer(1)
it.mSurfaceProxy.SurfaceQueueBuffer(0)
assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(2, 5000 /* ms */))
}
assertThat(trace).hasFrameSequence("SurfaceView", 1..2L)
}
@Test
fun testSetBufferScalingMode_multipleDequeueBuffer() {
val numFrames = 20L
val trace = withTrace {
for (count in 1..(numFrames / 2)) {
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
it.mSurfaceProxy.SurfaceQueueBuffer(0)
it.mSurfaceProxy.SurfaceQueueBuffer(1)
}
assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
}
assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
@Test
fun testSetBufferCount_queueMaxBufferCountMinusOne() {
val numBufferCount = 8
val numFrames = numBufferCount * 5L
val trace = withTrace {
assertEquals(0, it.mSurfaceProxy.NativeWindowSetBufferCount(numBufferCount + 1))
for (i in 1..numFrames / numBufferCount) {
for (bufferSlot in 0..numBufferCount - 1) {
assertEquals(0,
it.mSurfaceProxy.SurfaceDequeueBuffer(bufferSlot, 1000 /* ms */))
}
for (bufferSlot in 0..numBufferCount - 1) {
it.mSurfaceProxy.SurfaceQueueBuffer(bufferSlot)
}
}
assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
}
assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.graphics.Point
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
@Test
fun testSetBuffersGeometry_0x0_rejectsBuffer() {
val trace = withTrace {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
R8G8B8A8_UNORM)
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM)
// Submit buffer one with a different size which should be rejected
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
// submit a buffer with the default buffer size
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */)
}
// Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
assertThat(trace).layer("SurfaceView", 2).doesNotExist()
// Verify the next buffer is submitted with the correct size
assertThat(trace).layer("SurfaceView", 3).also {
it.hasBufferSize(defaultBufferSize)
// scaling mode is not passed down to the layer for blast
if (useBlastAdapter) {
it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
} else {
it.hasScalingMode(ScalingMode.FREEZE.ordinal)
}
}
}
@Test
fun testSetBufferScalingMode_freeze() {
val bufferSize = Point(300, 200)
val trace = withTrace {
it.drawFrame()
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
R8G8B8A8_UNORM)
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
// Change buffer size and set scaling mode to freeze
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
R8G8B8A8_UNORM)
// first dequeued buffer does not have the new size so it should be rejected.
it.mSurfaceProxy.SurfaceQueueBuffer(0)
it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
it.mSurfaceProxy.SurfaceQueueBuffer(1)
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
}
// verify buffer size is reset to default buffer size
assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
assertThat(trace).layer("SurfaceView", 2).doesNotExist()
assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
}
@Test
fun testSetBufferScalingMode_freeze_withBufferRotation() {
val rotatedBufferSize = Point(defaultBufferSize.y, defaultBufferSize.x)
val trace = withTrace {
it.drawFrame()
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, rotatedBufferSize,
R8G8B8A8_UNORM)
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
// Change buffer size and set scaling mode to freeze
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
R8G8B8A8_UNORM)
// first dequeued buffer does not have the new size so it should be rejected.
it.mSurfaceProxy.SurfaceQueueBuffer(0)
// add a buffer transform so the buffer size is correct.
it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.ROT_90)
it.mSurfaceProxy.SurfaceQueueBuffer(1)
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
}
// verify buffer size is reset to default buffer size
assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
assertThat(trace).layer("SurfaceView", 2).doesNotExist()
assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
assertThat(trace).layer("SurfaceView", 3).hasBufferOrientation(Transform.ROT_90.value)
}
@Test
fun testRejectedBuffersAreReleased() {
val bufferSize = Point(300, 200)
val trace = withTrace {
for (count in 0 until 5) {
it.drawFrame()
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed((count * 3) + 1L,
500 /* ms */), 0)
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
R8G8B8A8_UNORM)
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
// Change buffer size and set scaling mode to freeze
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
R8G8B8A8_UNORM)
// first dequeued buffer does not have the new size so it should be rejected.
it.mSurfaceProxy.SurfaceQueueBuffer(0)
it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
it.mSurfaceProxy.SurfaceQueueBuffer(1)
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed((count * 3) + 3L,
500 /* ms */), 0)
}
}
for (count in 0 until 5) {
assertThat(trace).layer("SurfaceView", (count * 3) + 1L)
.hasBufferSize(defaultBufferSize)
assertThat(trace).layer("SurfaceView", (count * 3) + 2L)
.doesNotExist()
assertThat(trace).layer("SurfaceView", (count * 3) + 3L)
.hasBufferSize(bufferSize)
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.graphics.Point
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
@Test
fun testSetBuffersGeometry_0x0_resetsBufferSize() {
val trace = withTrace {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0,
R8G8B8A8_UNORM)
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
}
// verify buffer size is reset to default buffer size
assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
}
@Test
fun testSetBuffersGeometry_smallerThanBuffer() {
val bufferSize = Point(300, 200)
val trace = withTrace {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
R8G8B8A8_UNORM)
it.drawFrame()
it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
}
assertThat(trace).layer("SurfaceView", 1).also {
it.hasBufferSize(bufferSize)
it.hasLayerSize(defaultBufferSize)
it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
}
}
@Test
fun testSetBuffersGeometry_largerThanBuffer() {
val bufferSize = Point(3000, 2000)
val trace = withTrace {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
R8G8B8A8_UNORM)
it.drawFrame()
it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */)
}
assertThat(trace).layer("SurfaceView", 1).also {
it.hasBufferSize(bufferSize)
it.hasLayerSize(defaultBufferSize)
it.hasScalingMode(ScalingMode.SCALE_TO_WINDOW.ordinal)
}
}
@Test
fun testSetBufferScalingMode_freeze() {
val bufferSize = Point(300, 200)
val trace = withTrace {
it.drawFrame()
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize,
R8G8B8A8_UNORM)
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
// Change buffer size and set scaling mode to freeze
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
R8G8B8A8_UNORM)
// first dequeued buffer does not have the new size so it should be rejected.
it.mSurfaceProxy.SurfaceQueueBuffer(0)
it.mSurfaceProxy.SurfaceSetScalingMode(ScalingMode.SCALE_TO_WINDOW)
it.mSurfaceProxy.SurfaceQueueBuffer(1)
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
}
// verify buffer size is reset to default buffer size
assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
assertThat(trace).layer("SurfaceView", 2).doesNotExist()
assertThat(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize)
}
@Test
fun testSetBuffersTransform_FLIP() {
val transforms = arrayOf(Transform.FLIP_H, Transform.FLIP_V, Transform.ROT_180).withIndex()
for ((index, transform) in transforms) {
val trace = withTrace {
it.mSurfaceProxy.ANativeWindowSetBuffersTransform(transform)
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
it.mSurfaceProxy.waitUntilBufferDisplayed(index + 1L, 500 /* ms */)
}
assertThat(trace).layer("SurfaceView", index + 1L).also {
it.hasBufferSize(defaultBufferSize)
it.hasLayerSize(defaultBufferSize)
it.hasBufferOrientation(transform.value)
}
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.graphics.Point
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
import junit.framework.Assert.assertEquals
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
class InverseDisplayTransformTests(useBlastAdapter: Boolean) :
SurfaceTracingTestBase(useBlastAdapter) {
@Before
override fun setup() {
scenarioRule.getScenario().onActivity {
it.rotate90()
}
instrumentation.waitForIdleSync()
super.setup()
}
@Test
fun testSetBufferScalingMode_freeze_withInvDisplayTransform() {
assumeFalse("Blast does not support buffer rejection with Inv display " +
"transform since the only user for this hidden api is camera which does not use" +
"fixed scaling mode.", useBlastAdapter)
val rotatedBufferSize = Point(defaultBufferSize.y, defaultBufferSize.x)
val trace = withTrace {
// Inverse display transforms are sticky AND they are only consumed by the sf after
// a valid buffer has been acquired.
it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.INVERSE_DISPLAY.value)
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
it.mSurfaceProxy.SurfaceQueueBuffer(0)
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(1, 500 /* ms */), 0)
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, rotatedBufferSize,
R8G8B8A8_UNORM)
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1000 /* ms */))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(1, 1000 /* ms */))
// Change buffer size and set scaling mode to freeze
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, Point(0, 0),
R8G8B8A8_UNORM)
// first dequeued buffer does not have the new size so it should be rejected.
it.mSurfaceProxy.ANativeWindowSetBuffersTransform(Transform.ROT_90.value)
it.mSurfaceProxy.SurfaceQueueBuffer(0)
it.mSurfaceProxy.ANativeWindowSetBuffersTransform(0)
it.mSurfaceProxy.SurfaceQueueBuffer(1)
assertEquals(it.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */), 0)
}
// verify buffer size is reset to default buffer size
assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
assertThat(trace).layer("SurfaceView", 2).doesNotExist()
assertThat(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize)
}
}

View File

@@ -15,51 +15,80 @@
*/
package com.android.test
import android.app.Activity
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Point
import android.graphics.Rect
import android.os.Bundle
import android.view.Gravity
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.View
import android.view.WindowManager
import android.view.cts.surfacevalidator.CapturedActivity
import android.widget.FrameLayout
import java.util.concurrent.CountDownLatch
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
class MainActivity : Activity() {
class MainActivity : CapturedActivity() {
val mSurfaceProxy = SurfaceProxy()
private var mSurfaceHolder: SurfaceHolder? = null
private val mDrawLock = ReentrantLock()
var mSurfaceView: SurfaceView? = null
val surface: Surface? get() = mSurfaceHolder?.surface
public override fun onCreate(savedInstanceState: Bundle?) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addSurfaceView(Rect(0, 0, 500, 200))
addSurfaceView(Point(500, 200))
window.decorView.apply {
systemUiVisibility =
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN
}
}
fun addSurfaceView(size: Rect): CountDownLatch {
override fun getCaptureDurationMs(): Long {
return 30000
}
fun addSurfaceView(size: Point): CountDownLatch {
val layout = findViewById<FrameLayout>(android.R.id.content)
val surfaceReadyLatch = CountDownLatch(1)
val surfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch)
layout.addView(surfaceView,
FrameLayout.LayoutParams(size.width(), size.height(), Gravity.TOP or Gravity.LEFT)
mSurfaceView = createSurfaceView(applicationContext, size, surfaceReadyLatch)
layout.addView(mSurfaceView!!,
FrameLayout.LayoutParams(size.x, size.y, Gravity.TOP or Gravity.LEFT)
.also { it.setMargins(100, 100, 0, 0) })
return surfaceReadyLatch
}
fun enableSeamlessRotation() {
val p: WindowManager.LayoutParams = window.attributes
p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS
window.attributes = p
}
fun rotate90() {
if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
}
}
private fun createSurfaceView(
context: Context,
size: Rect,
size: Point,
surfaceReadyLatch: CountDownLatch
): SurfaceView {
val surfaceView = SurfaceView(context)
surfaceView.setWillNotDraw(false)
surfaceView.holder.setFixedSize(size.width(), size.height())
surfaceView.holder.setFixedSize(size.x, size.y)
surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
mDrawLock.withLock {

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.annotation.ColorInt
import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.server.wm.WindowManagerState.getLogicalDisplaySize
import android.view.cts.surfacevalidator.CapturedActivity
import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase
import android.view.cts.surfacevalidator.PixelChecker
import android.view.cts.surfacevalidator.RectChecker
import android.widget.FrameLayout
import androidx.test.rule.ActivityTestRule
import org.junit.After
import org.junit.Before
import org.junit.Rule
import java.util.concurrent.CountDownLatch
open class ScreenRecordTestBase(useBlastAdapter: Boolean) :
SurfaceViewBufferTestBase(useBlastAdapter) {
@get:Rule
var mActivityRule = ActivityTestRule(MainActivity::class.java)
private lateinit var mActivity: MainActivity
@Before
override fun setup() {
super.setup()
mActivity = mActivityRule.launchActivity(Intent())
lateinit var surfaceReadyLatch: CountDownLatch
runOnUiThread {
it.dismissPermissionDialog()
it.setLogicalDisplaySize(getLogicalDisplaySize())
surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
}
surfaceReadyLatch.await()
// sleep to finish animations
instrumentation.waitForIdleSync()
}
@After
override fun teardown() {
super.teardown()
mActivityRule.finishActivity()
}
fun runOnUiThread(predicate: (it: MainActivity) -> Unit) {
mActivityRule.runOnUiThread {
predicate(mActivity)
}
}
fun withScreenRecording(
boundsToCheck: Rect,
@ColorInt color: Int,
predicate: (it: MainActivity) -> Unit
): CapturedActivity.TestResult {
val testCase = object : ISurfaceValidatorTestCase {
override fun getChecker(): PixelChecker = RectChecker(boundsToCheck, color)
override fun start(context: Context, parent: FrameLayout) {
predicate(mActivity)
}
override fun end() { /* do nothing */ }
}
return mActivity.runTest(testCase)
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.graphics.Color
import android.graphics.Rect
import android.os.SystemClock
import android.view.cts.surfacevalidator.PixelColor
import junit.framework.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
class SharedBufferModeScreenRecordTests(useBlastAdapter: Boolean) :
ScreenRecordTestBase(useBlastAdapter) {
/** When auto refresh is set, surface flinger will wake up and refresh the display presenting
* the latest content in the buffer.
*/
@Test
fun testAutoRefresh() {
var svBounds = Rect()
runOnUiThread {
assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
assertEquals(0, it.mSurfaceProxy.NativeWindowSetAutoRefresh(true))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
it.mSurfaceProxy.SurfaceQueueBuffer(0, false /* freeSlot */)
assertEquals(0,
it.mSurfaceProxy.waitUntilBufferDisplayed(1, 5000 /* ms */))
svBounds = Rect(0, 0, it.mSurfaceView!!.width, it.mSurfaceView!!.height)
val position = Rect()
it.mSurfaceView!!.getBoundsOnScreen(position)
svBounds.offsetTo(position.left, position.top)
// wait for buffers from other layers to be latched and transactions to be processed before
// updating the buffer
SystemClock.sleep(4000)
}
val result = withScreenRecording(svBounds, PixelColor.RED) {
it.mSurfaceProxy.drawBuffer(0, Color.RED)
}
val failRatio = 1.0f * result.failFrames / (result.failFrames + result.passFrames)
assertTrue("Error: " + result.failFrames +
" incorrect frames observed (out of " + (result.failFrames + result.passFrames) +
" frames)", failRatio < 0.05)
assertTrue("Error: Did not receive sufficient frame updates expected: >1000 actual:" +
result.passFrames, result.passFrames > 1000)
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.graphics.Color
import android.graphics.Rect
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
import junit.framework.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastAdapter) {
/** Sanity test to check each buffer is presented if its submitted with enough delay
* for SF to present the buffers. */
@Test
fun testCanPresentBuffers() {
val numFrames = 15L
val trace = withTrace {
assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
for (i in 1..numFrames) {
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
it.mSurfaceProxy.SurfaceQueueBuffer(0)
assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(i, 5000 /* ms */))
}
}
assertThat(trace).hasFrameSequence("SurfaceView", 1..numFrames)
}
/** Submit buffers as fast as possible testing that we are not blocked when dequeuing the buffer
* by setting the dequeue timeout to 1ms and checking that we present the newest buffer. */
@Test
fun testFastQueueBuffers() {
val numFrames = 15L
val trace = withTrace {
assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
for (i in 1..numFrames) {
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
it.mSurfaceProxy.SurfaceQueueBuffer(0)
}
assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(numFrames, 5000 /* ms */))
}
assertThat(trace).hasFrameSequence("SurfaceView", numFrames..numFrames)
}
/** Keep overwriting the buffer without queuing buffers and check that we present the latest
* buffer content. */
@Test
fun testAutoRefresh() {
var svBounds = Rect()
runOnUiThread {
assertEquals(0, it.mSurfaceProxy.NativeWindowSetSharedBufferMode(true))
assertEquals(0, it.mSurfaceProxy.NativeWindowSetAutoRefresh(true))
assertEquals(0, it.mSurfaceProxy.SurfaceDequeueBuffer(0, 1 /* ms */))
it.mSurfaceProxy.SurfaceQueueBuffer(0, false /* freeSlot */)
assertEquals(0, it.mSurfaceProxy.waitUntilBufferDisplayed(1, 5000 /* ms */))
svBounds = Rect(0, 0, it.mSurfaceView!!.width, it.mSurfaceView!!.height)
val position = Rect()
it.mSurfaceView!!.getBoundsOnScreen(position)
svBounds.offsetTo(position.left, position.top)
}
runOnUiThread {
it.mSurfaceProxy.drawBuffer(0, Color.RED)
checkPixels(svBounds, Color.RED)
it.mSurfaceProxy.drawBuffer(0, Color.GREEN)
checkPixels(svBounds, Color.GREEN)
it.mSurfaceProxy.drawBuffer(0, Color.BLUE)
checkPixels(svBounds, Color.BLUE)
}
}
}

View File

@@ -16,17 +16,46 @@
package com.android.test
import android.annotation.ColorInt
import android.graphics.Color
import android.graphics.Point
import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode
import com.android.test.SurfaceViewBufferTestBase.Companion.Transform
class SurfaceProxy {
init {
System.loadLibrary("surface_jni")
}
external fun setSurface(surface: Any)
external fun waitUntilBufferDisplayed(frameNumber: Int, timeoutSec: Int)
external fun waitUntilBufferDisplayed(frameNumber: Long, timeoutMs: Int): Int
external fun draw()
fun drawBuffer(slot: Int, @ColorInt c: Int) {
drawBuffer(slot, intArrayOf(Color.red(c), Color.green(c), Color.blue(c), Color.alpha(c)))
}
external fun drawBuffer(slot: Int, color: IntArray)
// android/native_window.h functions
external fun ANativeWindowLock()
external fun ANativeWindowUnlockAndPost()
fun ANativeWindowSetBuffersGeometry(surface: Any, size: Point, format: Int) {
ANativeWindowSetBuffersGeometry(surface, size.x, size.y, format)
}
external fun ANativeWindowSetBuffersGeometry(surface: Any, width: Int, height: Int, format: Int)
fun ANativeWindowSetBuffersTransform(transform: Transform) {
ANativeWindowSetBuffersTransform(transform.value)
}
external fun ANativeWindowSetBuffersTransform(transform: Int)
// gui/Surface.h functions
fun SurfaceSetScalingMode(scalingMode: ScalingMode) {
SurfaceSetScalingMode(scalingMode.ordinal)
}
external fun SurfaceSetScalingMode(scalingMode: Int)
external fun SurfaceDequeueBuffer(slot: Int, timeoutMs: Int): Int
external fun SurfaceCancelBuffer(slot: Int)
external fun SurfaceQueueBuffer(slot: Int, freeSlot: Boolean = true)
external fun NativeWindowSetBufferCount(count: Int): Int
external fun NativeWindowSetSharedBufferMode(shared: Boolean): Int
external fun NativeWindowSetAutoRefresh(autoRefresh: Boolean): Int
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.annotation.ColorInt
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Rect
import android.util.Log
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.android.server.wm.flicker.monitor.LayersTraceMonitor
import com.android.server.wm.flicker.monitor.withSFTracing
import com.android.server.wm.flicker.traces.layers.LayersTrace
import junit.framework.Assert
import org.junit.After
import org.junit.Before
import org.junit.Rule
import java.io.FileOutputStream
import java.io.IOException
import java.util.concurrent.CountDownLatch
open class SurfaceTracingTestBase(useBlastAdapter: Boolean) :
SurfaceViewBufferTestBase(useBlastAdapter) {
@get:Rule
var scenarioRule: ActivityScenarioRule<MainActivity> =
ActivityScenarioRule<MainActivity>(MainActivity::class.java)
@Before
override fun setup() {
super.setup()
stopLayerTrace()
addSurfaceView()
}
@After
override fun teardown() {
super.teardown()
scenarioRule.getScenario().close()
}
fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace {
return withSFTracing(instrumentation, TRACE_FLAGS) {
scenarioRule.getScenario().onActivity {
predicate(it)
}
}
}
fun runOnUiThread(predicate: (it: MainActivity) -> Unit) {
scenarioRule.getScenario().onActivity {
predicate(it)
}
}
private fun addSurfaceView() {
lateinit var surfaceReadyLatch: CountDownLatch
scenarioRule.getScenario().onActivity {
surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
}
surfaceReadyLatch.await()
// sleep to finish animations
instrumentation.waitForIdleSync()
}
private fun stopLayerTrace() {
val tmpDir = instrumentation.targetContext.dataDir.toPath()
LayersTraceMonitor(tmpDir).stop()
}
fun checkPixels(bounds: Rect, @ColorInt color: Int) {
val screenshot = instrumentation.getUiAutomation().takeScreenshot()
val pixels = IntArray(screenshot.width * screenshot.height)
screenshot.getPixels(pixels, 0, screenshot.width, 0, 0, screenshot.width, screenshot.height)
for (i in bounds.left + 10..bounds.right - 10) {
for (j in bounds.top + 10..bounds.bottom - 10) {
val actualColor = pixels[j * screenshot.width + i]
if (actualColor != color) {
val screenshotPath = instrumentation.targetContext
.getExternalFilesDir(null)?.resolve("screenshot.png")
try {
FileOutputStream(screenshotPath).use { out ->
screenshot.compress(Bitmap.CompressFormat.PNG, 100, out)
}
Log.e("SurfaceViewBufferTests", "Bitmap written to $screenshotPath")
} catch (e: IOException) {
Log.e("SurfaceViewBufferTests", "Error writing bitmap to file", e)
}
}
Assert.assertEquals("Checking $bounds found mismatch $i,$j",
Color.valueOf(color), Color.valueOf(actualColor))
}
}
}
private companion object {
private const val TRACE_FLAGS =
(1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC
}
}

View File

@@ -1,185 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.app.Instrumentation
import android.graphics.Rect
import android.provider.Settings
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.monitor.LayersTraceMonitor
import com.android.server.wm.flicker.monitor.withSFTracing
import com.android.server.wm.flicker.traces.layers.LayersTraceSubject.Companion.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.util.concurrent.CountDownLatch
import kotlin.properties.Delegates
@RunWith(Parameterized::class)
class SurfaceViewBufferTest(val useBlastAdapter: Boolean) {
private var mInitialUseBlastConfig by Delegates.notNull<Int>()
@get:Rule
var scenarioRule: ActivityScenarioRule<MainActivity> =
ActivityScenarioRule<MainActivity>(MainActivity::class.java)
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
val defaultBufferSize = Rect(0, 0, 640, 480)
@Before
fun setup() {
mInitialUseBlastConfig = Settings.Global.getInt(instrumentation.context.contentResolver,
"use_blast_adapter_sv", 0)
val enable = if (useBlastAdapter) 1 else 0
Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv",
enable)
val tmpDir = instrumentation.targetContext.dataDir.toPath()
LayersTraceMonitor(tmpDir).stop()
lateinit var surfaceReadyLatch: CountDownLatch
scenarioRule.getScenario().onActivity {
surfaceReadyLatch = it.addSurfaceView(defaultBufferSize)
}
surfaceReadyLatch.await()
}
@After
fun teardown() {
scenarioRule.getScenario().close()
Settings.Global.putInt(instrumentation.context.contentResolver,
"use_blast_adapter_sv", mInitialUseBlastConfig)
}
@Test
fun testSetBuffersGeometry_0x0_resetsBufferSize() {
val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
scenarioRule.getScenario().onActivity {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0,
R8G8B8A8_UNORM)
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
}
}
// verify buffer size is reset to default buffer size
assertThat(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize)
}
@Test
fun testSetBuffersGeometry_0x0_rejectsBuffer() {
val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
scenarioRule.getScenario().onActivity {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
R8G8B8A8_UNORM)
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 0, 0, R8G8B8A8_UNORM)
// Submit buffer one with a different size which should be rejected
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
// submit a buffer with the default buffer size
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
it.mSurfaceProxy.waitUntilBufferDisplayed(3, 1 /* sec */)
}
}
// Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE
assertThat(trace).layer("SurfaceView", 2).doesNotExist()
// Verify the next buffer is submitted with the correct size
assertThat(trace).layer("SurfaceView", 3).also {
it.hasBufferSize(defaultBufferSize)
it.hasScalingMode(0 /* NATIVE_WINDOW_SCALING_MODE_FREEZE */)
}
}
@Test
fun testSetBuffersGeometry_smallerThanBuffer() {
val bufferSize = Rect(0, 0, 300, 200)
val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
scenarioRule.getScenario().onActivity {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
bufferSize.height(), R8G8B8A8_UNORM)
it.drawFrame()
it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
}
}
assertThat(trace).layer("SurfaceView", 1).also {
it.hasBufferSize(bufferSize)
it.hasLayerSize(defaultBufferSize)
it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
}
}
@Test
fun testSetBuffersGeometry_largerThanBuffer() {
val bufferSize = Rect(0, 0, 3000, 2000)
val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
scenarioRule.getScenario().onActivity {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, bufferSize.width(),
bufferSize.height(), R8G8B8A8_UNORM)
it.drawFrame()
it.mSurfaceProxy.waitUntilBufferDisplayed(1, 1 /* sec */)
}
}
assertThat(trace).layer("SurfaceView", 1).also {
it.hasBufferSize(bufferSize)
it.hasLayerSize(defaultBufferSize)
it.hasScalingMode(1 /* NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW */)
}
}
/** Submit buffers as fast as possible and make sure they are queued */
@Test
fun testQueueBuffers() {
val trace = withSFTracing(instrumentation, TRACE_FLAGS) {
scenarioRule.getScenario().onActivity {
it.mSurfaceProxy.ANativeWindowSetBuffersGeometry(it.surface!!, 100, 100,
R8G8B8A8_UNORM)
for (i in 0..100) {
it.mSurfaceProxy.ANativeWindowLock()
it.mSurfaceProxy.ANativeWindowUnlockAndPost()
}
it.mSurfaceProxy.waitUntilBufferDisplayed(100, 1 /* sec */)
}
}
for (frameNumber in 1..100) {
assertThat(trace).layer("SurfaceView", frameNumber.toLong())
}
}
companion object {
private const val TRACE_FLAGS = 0x1 // TRACE_CRITICAL
private const val R8G8B8A8_UNORM = 1
@JvmStatic
@Parameterized.Parameters(name = "blast={0}")
fun data(): Collection<Array<Any>> {
return listOf(
arrayOf(false), // First test: submit buffers via bufferqueue
arrayOf(true) // Second test: submit buffers via blast adapter
)
}
}
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.test
import android.app.Instrumentation
import android.graphics.Point
import android.provider.Settings
import androidx.test.InstrumentationRegistry
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.rules.TestName
import org.junit.runners.Parameterized
import kotlin.properties.Delegates
open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) {
private var mInitialBlastConfig by Delegates.notNull<Boolean>()
val instrumentation: Instrumentation
get() = InstrumentationRegistry.getInstrumentation()
@get:Rule
var mName = TestName()
@Before
open fun setup() {
mInitialBlastConfig = getBlastAdapterSvEnabled()
setBlastAdapterSvEnabled(useBlastAdapter)
}
@After
open fun teardown() {
setBlastAdapterSvEnabled(mInitialBlastConfig)
}
private fun getBlastAdapterSvEnabled(): Boolean {
return Settings.Global.getInt(instrumentation.context.contentResolver,
"use_blast_adapter_sv", 0) != 0
}
private fun setBlastAdapterSvEnabled(enable: Boolean) {
Settings.Global.putInt(instrumentation.context.contentResolver, "use_blast_adapter_sv",
if (enable) 1 else 0)
}
companion object {
@JvmStatic
@Parameterized.Parameters(name = "blast={0}")
fun data(): Collection<Array<Any>> {
return listOf(
arrayOf(false), // First test: submit buffers via bufferqueue
arrayOf(true) // Second test: submit buffers via blast adapter
)
}
const val R8G8B8A8_UNORM = 1
val defaultBufferSize = Point(640, 480)
// system/window.h definitions
enum class ScalingMode() {
FREEZE, // = 0
SCALE_TO_WINDOW, // =1
SCALE_CROP, // = 2
NO_SCALE_CROP // = 3
}
// system/window.h definitions
enum class Transform(val value: Int) {
/* flip source image horizontally */
FLIP_H(1),
/* flip source image vertically */
FLIP_V(2),
/* rotate source image 90 degrees clock-wise, and is applied after TRANSFORM_FLIP_{H|V} */
ROT_90(4),
/* rotate source image 180 degrees */
ROT_180(3),
/* rotate source image 270 degrees clock-wise */
ROT_270(7),
/* transforms source by the inverse transform of the screen it is displayed onto. This
* transform is applied last */
INVERSE_DISPLAY(0x08)
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.test.taskembed
import android.app.Instrumentation
import android.graphics.Point
import android.graphics.Rect
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.platform.app.InstrumentationRegistry
@@ -93,13 +94,15 @@ class ResizeTasksSyncTest {
// verify buffer size should be changed to expected values.
assertThat(trace).layer(FIRST_ACTIVITY, frame).also {
it.hasLayerSize(firstBounds)
it.hasBufferSize(firstBounds)
val firstTaskSize = Point(firstBounds.width(), firstBounds.height())
it.hasLayerSize(firstTaskSize)
it.hasBufferSize(firstTaskSize)
}
assertThat(trace).layer(SECOND_ACTIVITY, frame).also {
it.hasLayerSize(secondBounds)
it.hasBufferSize(secondBounds)
val secondTaskSize = Point(secondBounds.width(), secondBounds.height())
it.hasLayerSize(secondTaskSize)
it.hasBufferSize(secondTaskSize)
}
}