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 }