Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 460a46f9 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

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

parents 18d8293c a47f891f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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",
+11 −0
Original line number Diff line number Diff line
@@ -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>

+124 −3
Original line number Diff line number Diff line
@@ -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;
}
}
 No newline at end of file
+93 −0
Original line number Diff line number Diff line
/*
 * 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)
    }
}
 No newline at end of file
+151 −0
Original line number Diff line number Diff line
/*
 * 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)
        }
    }
}
 No newline at end of file
Loading