1 /*
<lambda>null2  * 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 
rendererTestnull116 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 
fetchMemoryInfonull131 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
testBasicDrawCpuConsumernull148     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
testBasicDrawGpuConsumernull197     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
<lambda>null240     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
testSetStoppednull256     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
testNoSurfacenull281     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
testClearContentnull307     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
testDestroynull350     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
testSpotShadowSetupnull379     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
testLotsOfBuffersnull414     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 }
487