Home | History | Annotate | Download | only in testclasses
      1 /*
      2  * Copyright (C) 2019 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.uirendering.cts.testclasses
     18 
     19 import android.graphics.Bitmap
     20 import android.graphics.Color
     21 import android.graphics.HardwareRenderer
     22 import android.graphics.Outline
     23 import android.graphics.Paint
     24 import android.graphics.PixelFormat
     25 import android.graphics.RecordingCanvas
     26 import android.graphics.Rect
     27 import android.graphics.RenderNode
     28 import android.hardware.HardwareBuffer
     29 import android.media.Image
     30 import android.media.ImageReader
     31 import android.os.Debug
     32 import android.uirendering.cts.bitmapverifiers.BitmapVerifier
     33 import android.uirendering.cts.bitmapverifiers.ColorVerifier
     34 import android.uirendering.cts.bitmapverifiers.PerPixelBitmapVerifier
     35 import android.uirendering.cts.bitmapverifiers.RectVerifier
     36 import android.uirendering.cts.bitmapverifiers.RegionVerifier
     37 import android.uirendering.cts.testinfrastructure.ActivityTestBase
     38 import android.uirendering.cts.util.CompareUtils
     39 import android.util.Log
     40 import androidx.test.filters.LargeTest
     41 import androidx.test.filters.MediumTest
     42 import androidx.test.runner.AndroidJUnit4
     43 import org.junit.Ignore
     44 import org.junit.Test
     45 import org.junit.runner.RunWith
     46 import kotlin.test.assertEquals
     47 import kotlin.test.assertFalse
     48 import kotlin.test.assertNotEquals
     49 import kotlin.test.assertNotNull
     50 import kotlin.test.assertTrue
     51 
     52 const val TEST_WIDTH = ActivityTestBase.TEST_WIDTH
     53 const val TEST_HEIGHT = ActivityTestBase.TEST_HEIGHT
     54 
     55 class CaptureResult(
     56     val bitmap: IntArray,
     57     val offset: Int,
     58     val stride: Int,
     59     val width: Int,
     60     val height: Int
     61 )
     62 
     63 data class RendererTest(
     64     var verifier: BitmapVerifier? = null,
     65     var onPrepare: (HardwareRenderer.() -> Unit)? = null,
     66     var onDraw: (RecordingCanvas.() -> Unit)? = null
     67 )
     68 
     69 private fun verify(verifier: BitmapVerifier, setup: HardwareRenderer.() -> Unit) {
     70     val reader = ImageReader.newInstance(
     71         TEST_WIDTH, TEST_HEIGHT, PixelFormat.RGBA_8888, 1,
     72         HardwareBuffer.USAGE_CPU_READ_OFTEN or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
     73     val renderer = HardwareRenderer()
     74     var image: Image? = null
     75     try {
     76         renderer.setSurface(reader.surface)
     77         renderer.notifyFramePending()
     78         setup.invoke(renderer)
     79         val syncResult = renderer.createRenderRequest()
     80             .setWaitForPresent(true)
     81             .syncAndDraw()
     82         assertEquals(HardwareRenderer.SYNC_OK, syncResult)
     83         image = reader.acquireNextImage()
     84         assertNotNull(image)
     85         val planes = image.planes
     86         assertNotNull(planes)
     87         assertEquals(1, planes.size)
     88         val plane = planes[0]
     89         assertEquals(4, plane.pixelStride)
     90         assertTrue((ActivityTestBase.TEST_WIDTH * 4) <= plane.rowStride)
     91         val buffer = plane.buffer
     92         val channels = ByteArray(buffer.remaining())
     93         buffer.get(channels, 0, channels.size)
     94         val pixels = IntArray(channels.size / 4)
     95         var pixelIndex = 0
     96         var channelIndex = 0
     97         // Need to switch from RGBA (the pixel format on the reader) to ARGB (what bitmaps use)
     98         while (channelIndex < channels.size) {
     99             val red = channels[channelIndex++].toInt() and 0xFF
    100             val green = channels[channelIndex++].toInt() and 0xFF
    101             val blue = channels[channelIndex++].toInt() and 0xFF
    102             val alpha = channels[channelIndex++].toInt() and 0xFF
    103             pixels[pixelIndex++] = Color.argb(alpha, red, green, blue)
    104         }
    105         val result = CaptureResult(pixels, 0, plane.rowStride / plane.pixelStride,
    106             TEST_WIDTH, TEST_WIDTH)
    107         assertTrue(verifier.verify(
    108             result.bitmap, result.offset, result.stride, result.width, result.height))
    109     } finally {
    110         image?.close()
    111         renderer.destroy()
    112         reader.close()
    113     }
    114 }
    115 
    116 private fun rendererTest(setup: RendererTest.() -> Unit) {
    117     val spec = RendererTest()
    118     setup.invoke(spec)
    119     assertNotNull(spec.verifier, "Missing BitmapVerifier")
    120     assertNotNull(spec.onDraw, "Missing onDraw callback")
    121     verify(spec.verifier!!) {
    122         spec.onPrepare?.invoke(this)
    123         val content = RenderNode("content")
    124         content.setPosition(0, 0, TEST_WIDTH, TEST_HEIGHT)
    125         spec.onDraw!!.invoke(content.beginRecording())
    126         content.endRecording()
    127         setContentRoot(content)
    128     }
    129 }
    130 
    131 private fun fetchMemoryInfo(): Debug.MemoryInfo {
    132     Runtime.getRuntime().apply {
    133         gc()
    134         runFinalization()
    135         gc()
    136         runFinalization()
    137         gc()
    138     }
    139     val meminfo = Debug.MemoryInfo()
    140     Debug.getMemoryInfo(meminfo)
    141     return meminfo
    142 }
    143 
    144 @MediumTest
    145 @RunWith(AndroidJUnit4::class)
    146 class HardwareRendererTests : ActivityTestBase() {
    147     @Test
    148     fun testBasicDrawCpuConsumer() {
    149         val reader = ImageReader.newInstance(TEST_WIDTH, TEST_HEIGHT, PixelFormat.RGBA_8888, 1,
    150             HardwareBuffer.USAGE_CPU_READ_OFTEN or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
    151         assertNotNull(reader)
    152         val renderer = HardwareRenderer()
    153         var image: Image? = null
    154 
    155         try {
    156             val content = RenderNode("content")
    157             content.setPosition(0, 0, TEST_WIDTH, TEST_HEIGHT)
    158             val canvas = content.beginRecording()
    159             canvas.drawColor(Color.BLUE)
    160             content.endRecording()
    161             renderer.setContentRoot(content)
    162 
    163             renderer.setSurface(reader.surface)
    164 
    165             val syncResult = renderer.createRenderRequest()
    166                 .setWaitForPresent(true)
    167                 .syncAndDraw()
    168 
    169             assertEquals(HardwareRenderer.SYNC_OK, syncResult)
    170 
    171             image = reader.acquireNextImage()
    172             assertNotNull(image)
    173             val planes = image.planes
    174             assertNotNull(planes)
    175             assertEquals(1, planes.size)
    176             val plane = planes[0]
    177             assertEquals(4, plane.pixelStride)
    178             assertTrue((TEST_WIDTH * 4) <= plane.rowStride)
    179 
    180             val buffer = plane.buffer
    181             val red = buffer.get()
    182             val green = buffer.get()
    183             val blue = buffer.get()
    184             val alpha = buffer.get()
    185             assertEquals(0, red, "red")
    186             assertEquals(0, green, "green")
    187             assertEquals(0xFF.toByte(), blue, "blue")
    188             assertEquals(0xFF.toByte(), alpha, "alpha")
    189         } finally {
    190             image?.close()
    191             renderer.destroy()
    192             reader.close()
    193         }
    194     }
    195 
    196     @Test
    197     fun testBasicDrawGpuConsumer() {
    198         val reader = ImageReader.newInstance(TEST_WIDTH, TEST_HEIGHT, PixelFormat.RGBA_8888, 1,
    199             HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
    200         assertNotNull(reader)
    201         val renderer = HardwareRenderer()
    202         var image: Image? = null
    203 
    204         try {
    205             val content = RenderNode("content")
    206             content.setPosition(0, 0, TEST_WIDTH, TEST_HEIGHT)
    207             val canvas = content.beginRecording()
    208             canvas.drawColor(Color.BLUE)
    209             content.endRecording()
    210             renderer.setContentRoot(content)
    211 
    212             renderer.setSurface(reader.surface)
    213 
    214             val syncResult = renderer.createRenderRequest()
    215                 .setWaitForPresent(true)
    216                 .syncAndDraw()
    217 
    218             assertEquals(HardwareRenderer.SYNC_OK, syncResult)
    219 
    220             image = reader.acquireNextImage()
    221             assertNotNull(image)
    222             val buffer = image.hardwareBuffer
    223             assertNotNull(buffer)
    224             Log.d("HardwareRenderer", "buffer usage bits: " +
    225                     java.lang.Long.toHexString(buffer.usage))
    226             val bitmap = Bitmap.wrapHardwareBuffer(buffer, null)
    227                 .copy(Bitmap.Config.ARGB_8888, false)
    228 
    229             assertEquals(TEST_WIDTH, bitmap.width)
    230             assertEquals(TEST_HEIGHT, bitmap.height)
    231             assertEquals(0xFF0000FF.toInt(), bitmap.getPixel(0, 0))
    232         } finally {
    233             image?.close()
    234             renderer.destroy()
    235             reader.close()
    236         }
    237     }
    238 
    239     @Test
    240     fun testSetOpaque() = rendererTest {
    241         val rect = Rect(10, 10, 30, 30)
    242         onPrepare = {
    243             assertTrue(isOpaque)
    244             isOpaque = false
    245             assertFalse(isOpaque)
    246         }
    247         onDraw = {
    248             val paint = Paint()
    249             paint.color = Color.RED
    250             drawRect(rect, paint)
    251         }
    252         verifier = RectVerifier(Color.TRANSPARENT, Color.RED, rect)
    253     }
    254 
    255     @Test
    256     fun testSetStopped() {
    257         val reader = ImageReader.newInstance(TEST_WIDTH, TEST_HEIGHT, PixelFormat.RGBA_8888, 1,
    258             HardwareBuffer.USAGE_CPU_READ_OFTEN or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
    259         assertNotNull(reader)
    260         val renderer = HardwareRenderer()
    261         try {
    262             renderer.setSurface(reader.surface)
    263             assertEquals(HardwareRenderer.SYNC_OK,
    264                 renderer.createRenderRequest().syncAndDraw())
    265             renderer.stop()
    266             assertEquals(HardwareRenderer.SYNC_CONTEXT_IS_STOPPED
    267                     or HardwareRenderer.SYNC_FRAME_DROPPED,
    268                 renderer.createRenderRequest().syncAndDraw())
    269             reader.acquireLatestImage()?.close()
    270             renderer.start()
    271             val result = renderer.createRenderRequest().syncAndDraw()
    272             assertEquals(0, result and HardwareRenderer.SYNC_CONTEXT_IS_STOPPED)
    273         } finally {
    274             renderer.destroy()
    275             reader.close()
    276         }
    277     }
    278 
    279     @Test
    280     @Ignore // TODO: Re-enable, see b/124520175
    281     fun testNoSurface() {
    282         val reader = ImageReader.newInstance(TEST_WIDTH, TEST_HEIGHT, PixelFormat.RGBA_8888, 1,
    283             HardwareBuffer.USAGE_CPU_READ_OFTEN or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
    284         assertNotNull(reader)
    285         val renderer = HardwareRenderer()
    286         try {
    287             assertEquals(
    288                 HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND
    289                         or HardwareRenderer.SYNC_FRAME_DROPPED,
    290                 renderer.createRenderRequest().syncAndDraw())
    291             renderer.setSurface(reader.surface)
    292             assertEquals(HardwareRenderer.SYNC_OK,
    293                 renderer.createRenderRequest().syncAndDraw())
    294             reader.close()
    295             Thread.sleep(32)
    296             assertEquals(HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND
    297                     or HardwareRenderer.SYNC_FRAME_DROPPED,
    298                 renderer.createRenderRequest().syncAndDraw())
    299         } finally {
    300             renderer.destroy()
    301             reader.close()
    302         }
    303     }
    304 
    305     @LargeTest
    306     @Test
    307     fun testClearContent() {
    308         val reader = ImageReader.newInstance(TEST_WIDTH, TEST_HEIGHT, PixelFormat.RGBA_8888, 1,
    309             HardwareBuffer.USAGE_CPU_READ_OFTEN or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
    310         assertNotNull(reader)
    311         val renderer = HardwareRenderer()
    312         val content = RenderNode("content")
    313         val canvas = content.beginRecording()
    314 
    315         run {
    316             for (i in 0..5) {
    317                 val bitmap = Bitmap.createBitmap(1024, 1024, Bitmap.Config.ARGB_8888)
    318                 bitmap.eraseColor(Color.RED)
    319                 canvas.drawBitmap(bitmap, 0f, 0f, Paint())
    320             }
    321         }
    322 
    323         content.endRecording()
    324         try {
    325             renderer.setSurface(reader.surface)
    326             renderer.setContentRoot(content)
    327             assertEquals(HardwareRenderer.SYNC_OK,
    328                 renderer.createRenderRequest()
    329                     .setWaitForPresent(true)
    330                     .syncAndDraw())
    331             val infoBeforeClear = fetchMemoryInfo()
    332             renderer.clearContent()
    333             val infoAfterClear = fetchMemoryInfo()
    334             assertNotEquals(0, infoBeforeClear.totalPss)
    335             assertNotEquals(0, infoAfterClear.totalPss)
    336 
    337             val pssDifference = infoBeforeClear.totalPss - infoAfterClear.totalPss
    338             // Use a rather generous margin of error in case the only thing freed is the bitmap
    339             // while other memroy was allocated in the process of checking that. pss is in kB
    340             val minimalDifference = 5 * 1024
    341             assertTrue(pssDifference > minimalDifference,
    342                 "pssDifference: $pssDifference less than expected of at least $minimalDifference")
    343         } finally {
    344             renderer.destroy()
    345             reader.close()
    346         }
    347     }
    348 
    349     @Test
    350     fun testDestroy() {
    351         val reader = ImageReader.newInstance(TEST_WIDTH, TEST_HEIGHT, PixelFormat.RGBA_8888, 1,
    352             HardwareBuffer.USAGE_CPU_READ_OFTEN or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
    353         assertNotNull(reader)
    354         val renderer = HardwareRenderer()
    355         try {
    356             renderer.setSurface(reader.surface)
    357             assertEquals(HardwareRenderer.SYNC_OK,
    358                 renderer.createRenderRequest()
    359                     .setWaitForPresent(true)
    360                     .syncAndDraw())
    361             renderer.destroy()
    362             assertEquals(
    363                 HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND
    364                         or HardwareRenderer.SYNC_FRAME_DROPPED,
    365                 renderer.createRenderRequest().syncAndDraw())
    366 
    367             renderer.setSurface(reader.surface)
    368             assertEquals(HardwareRenderer.SYNC_OK,
    369                 renderer.createRenderRequest()
    370                     .setWaitForPresent(true)
    371                     .syncAndDraw())
    372         } finally {
    373             renderer.destroy()
    374             reader.close()
    375         }
    376     }
    377 
    378     @Test
    379     fun testSpotShadowSetup() = rendererTest {
    380         val childRect = Rect(25, 25, 65, 65)
    381         onPrepare = {
    382             setLightSourceAlpha(0.0f, 1.0f)
    383             setLightSourceGeometry(TEST_WIDTH / 2f, 0f, 800.0f, 20.0f)
    384         }
    385         onDraw = {
    386             val childNode = RenderNode("shadowCaster")
    387             childNode.setPosition(childRect)
    388             val outline = Outline()
    389             outline.setRect(Rect(0, 0, childRect.width(), childRect.height()))
    390             outline.alpha = 1f
    391             childNode.setOutline(outline)
    392             val childCanvas = childNode.beginRecording()
    393             childCanvas.drawColor(Color.RED)
    394             childNode.endRecording()
    395             childNode.elevation = 20f
    396 
    397             drawColor(Color.WHITE)
    398             enableZ()
    399             drawRenderNode(childNode)
    400             disableZ()
    401         }
    402         verifier = RegionVerifier()
    403             .addVerifier(childRect, ColorVerifier(Color.RED, 10))
    404             .addVerifier(
    405                 Rect(childRect.left, childRect.bottom, childRect.right, childRect.bottom + 10),
    406                 object : PerPixelBitmapVerifier() {
    407                     override fun verifyPixel(x: Int, y: Int, observedColor: Int): Boolean {
    408                         return CompareUtils.verifyPixelGrayScale(observedColor, 1)
    409                     }
    410                 })
    411     }
    412 
    413     @Test
    414     fun testLotsOfBuffers() {
    415         val colorForIndex = { i: Int ->
    416             Color.argb(255, 10 * i, 6 * i, 2 * i)
    417         }
    418         val testColors = IntArray(20, colorForIndex)
    419 
    420         val reader = ImageReader.newInstance(
    421                 TEST_WIDTH, TEST_HEIGHT, PixelFormat.RGBA_8888, testColors.size,
    422                 HardwareBuffer.USAGE_CPU_READ_OFTEN or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
    423         assertNotNull(reader)
    424         val renderer = HardwareRenderer()
    425         val images = ArrayList<Image>()
    426 
    427         try {
    428             val content = RenderNode("content")
    429             content.setPosition(0, 0, TEST_WIDTH, TEST_HEIGHT)
    430             renderer.setContentRoot(content)
    431             renderer.setSurface(reader.surface)
    432 
    433             testColors.forEach {
    434                 val canvas = content.beginRecording()
    435                 canvas.drawColor(it)
    436                 content.endRecording()
    437 
    438                 val syncResult = renderer.createRenderRequest()
    439                         .setWaitForPresent(true)
    440                         .syncAndDraw()
    441                 assertEquals(HardwareRenderer.SYNC_OK, syncResult)
    442                 // TODO: Add API to avoid this
    443                 Thread.sleep(32)
    444             }
    445 
    446             for (i in 0 until testColors.size) {
    447                 val image = reader.acquireNextImage()
    448                 assertNotNull(image)
    449                 images.add(image)
    450             }
    451 
    452             assertEquals(testColors.size, images.size)
    453 
    454             images.forEachIndexed { index, image ->
    455                 val planes = image.planes
    456                 assertNotNull(planes)
    457                 assertEquals(1, planes.size)
    458                 val plane = planes[0]
    459                 assertEquals(4, plane.pixelStride)
    460                 assertTrue((TEST_WIDTH * 4) <= plane.rowStride)
    461 
    462                 val buffer = plane.buffer
    463                 val red = buffer.get().toInt() and 0xFF
    464                 val green = buffer.get().toInt() and 0xFF
    465                 val blue = buffer.get().toInt() and 0xFF
    466                 val alpha = buffer.get().toInt() and 0xFF
    467 
    468                 val expectedColor = colorForIndex(index)
    469 
    470                 assertEquals(Color.red(expectedColor), red, "red")
    471                 assertEquals(Color.green(expectedColor), green, "green")
    472                 assertEquals(Color.blue(expectedColor), blue, "blue")
    473                 assertEquals(255, alpha, "alpha")
    474             }
    475         } finally {
    476             images.forEach {
    477                 try {
    478                     it.close()
    479                 } catch (ex: Throwable) {}
    480             }
    481             images.clear()
    482             renderer.destroy()
    483             reader.close()
    484         }
    485     }
    486 }