/* * Copyright (C) 2021 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. */ // TODO Rename to something better package com.example.testapp import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.renderscript.RenderScript import android.renderscript.toolkit.BlendingMode import android.renderscript.toolkit.LookupTable import android.renderscript.toolkit.Range2d import android.renderscript.toolkit.Rgba3dArray import android.renderscript.toolkit.Toolkit import android.renderscript.toolkit.YuvFormat import kotlin.math.abs import kotlin.math.min data class TestLayout( val sizeX: Int, val sizeY: Int, val restriction: Range2d? ) // List of dimensions (sizeX, sizeY) to try when generating random data. val commonLayoutsToTry = listOf( // Small layouts to start with TestLayout(3, 4, null), TestLayout(3, 4, Range2d(0, 1, 0, 3)), TestLayout(3, 4, Range2d(2, 3, 1, 4)), TestLayout(10, 14, null), TestLayout(10, 14, Range2d(2, 3, 8, 14)), // The size of most CTS intrinsic tests TestLayout(160, 100, null), TestLayout(125, 227, Range2d(50, 125, 100, 227)), // A larger one TestLayout(800, 600, null), // Weirdly shaped ones TestLayout(1, 1, null), // A single item // TODO This size makes Intrinsic Blur fail. TestLayout(16000, 1, null), // A single item TestLayout(1, 16000, null), // One large row // A very large test TestLayout(1024, 2048, null), ) class Tester(context: Context, private val validate: Boolean) { private val renderscriptContext = RenderScript.create(context) private val toolkit = Toolkit() private val testImage1 = BitmapFactory.decodeResource(context.resources, R.drawable.img800x450a) private val testImage2 = BitmapFactory.decodeResource(context.resources, R.drawable.img800x450b) init { validateTestImage(testImage1) validateTestImage(testImage2) } /** * Verify that the test images are in format that works for our tests. */ private fun validateTestImage(bitmap: Bitmap) { require(bitmap.config == Bitmap.Config.ARGB_8888) require(bitmap.rowBytes == bitmap.width * 4) { "Can't handle bitmaps that have extra padding. " + "${bitmap.rowBytes} != ${bitmap.width} * 4." } require(bitmap.byteCount == bitmap.rowBytes * bitmap.height) } fun destroy() { renderscriptContext.destroy() } @ExperimentalUnsignedTypes fun testAll(timer: TimingTracker): String { val tests = listOf( Pair("blend", ::testBlend), Pair("blur", ::testBlur), Pair("colorMatrix", ::testColorMatrix), Pair("convolve", ::testConvolve), Pair("histogram", ::testHistogram), Pair("lut", ::testLut), Pair("lut3d", ::testLut3d), Pair("resize", ::testResize), Pair("yuvToRgb", ::testYuvToRgb), ) val results = Array(tests.size) { "" } for (i in tests.indices) { val (name, test) = tests[i] println("Doing $name") val success = test(timer) results[i] = "$name " + if (success) "succeeded" else "FAILED! FAILED! FAILED! FAILED!" println(" ${results[i]}") } return results.joinToString("\n") } @ExperimentalUnsignedTypes private fun testBlend(timer: TimingTracker): Boolean { return BlendingMode.values().all { mode -> testOneBitmapBlend(timer, testImage1, testImage2, mode, null) and testOneBitmapBlend( timer, testImage1, testImage2, mode, Range2d(6, 23, 2, 4) ) and commonLayoutsToTry.all { (sizeX, sizeY, restriction) -> testOneRandomBlend(timer, sizeX, sizeY, mode, restriction) } } } @ExperimentalUnsignedTypes private fun testOneRandomBlend( timer: TimingTracker, sizeX: Int, sizeY: Int, mode: BlendingMode, restriction: Range2d? ): Boolean { val sourceArray = randomByteArray(0x50521f0, sizeX, sizeY, 4) val destArray = randomByteArray(0x2932147, sizeX, sizeY, 4) // Make clones because these will be modified by the blend. val intrinsicDestArray = destArray.clone() val referenceDestArray = destArray.clone() val toolkitDestArray = destArray.clone() timer.measure("IntrinsicBlend") { intrinsicBlend( renderscriptContext, mode, sourceArray, intrinsicDestArray, sizeX, sizeY, restriction ) } timer.measure("ToolkitBlend") { toolkit.blend(mode, sourceArray, toolkitDestArray, sizeX, sizeY, restriction) } if (!validate) return true timer.measure("ReferenceBlend") { referenceBlend(mode, sourceArray, referenceDestArray, sizeX, sizeY, restriction) } return validateSame( "Blend_$mode", intrinsicDestArray, referenceDestArray, toolkitDestArray ) { println("blend $mode ($sizeX, $sizeY) $restriction") logArray("Blend_$mode src", sourceArray, 48) logArray("Blend_$mode dst", destArray, 48) logArray("Blend_$mode reference out", referenceDestArray, 48) logArray("Blend_$mode intrinsic out", intrinsicDestArray, 48) logArray("Blend_$mode toolkit out", toolkitDestArray, 48) } } @ExperimentalUnsignedTypes private fun testOneBitmapBlend( timer: TimingTracker, sourceBitmap: Bitmap, destBitmap: Bitmap, mode: BlendingMode, restriction: Range2d? ): Boolean { // Make clones because these will be modified by the blend. val intrinsicDestBitmap = duplicateBitmap(destBitmap) val toolkitDestBitmap = duplicateBitmap(destBitmap) val referenceDestBitmap = duplicateBitmap(destBitmap) timer.measure("IntrinsicBlend") { intrinsicBlend( renderscriptContext, mode, sourceBitmap, intrinsicDestBitmap, restriction ) } timer.measure("ToolkitBlend") { toolkit.blend(mode, sourceBitmap, toolkitDestBitmap, restriction) } if (!validate) return true val referenceDestArray = getBitmapBytes(referenceDestBitmap) timer.measure("ReferenceBlend") { referenceBlend( mode, getBitmapBytes(sourceBitmap), referenceDestArray, sourceBitmap.width, sourceBitmap.height, restriction ) } val intrinsicDestArray = getBitmapBytes(intrinsicDestBitmap) val toolkitDestArray = getBitmapBytes(toolkitDestBitmap) return validateSame( "BlendBitmap_$mode", intrinsicDestArray, referenceDestArray, toolkitDestArray ) { println("BlendBitmap $mode $restriction") //logArray("BlendBitmap_$mode src", sourceArray, 48) //logArray("BlendBitmap_$mode dst", destArray, 48) logArray("BlendBitmap_$mode reference out", referenceDestArray, 48) logArray("BlendBitmap_$mode intrinsic out", intrinsicDestArray, 48) logArray("BlendBitmap_$mode toolkit out", toolkitDestArray, 48) } } @ExperimentalUnsignedTypes private fun testBlur(timer: TimingTracker): Boolean { return arrayOf(1, 3, 8, 25).all { radius -> testOneBitmapBlur(timer, testImage1, radius, null) and testOneBitmapBlur(timer, testImage1, radius, Range2d(6, 23, 2, 4)) and commonLayoutsToTry.all { (sizeX, sizeY, restriction) -> arrayOf(1, 4).all { vectorSize -> testOneRandomBlur(timer, vectorSize, sizeX, sizeY, radius, restriction) } } } } @ExperimentalUnsignedTypes private fun testOneRandomBlur( timer: TimingTracker, vectorSize: Int, sizeX: Int, sizeY: Int, radius: Int, restriction: Range2d? ): Boolean { val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, vectorSize) val intrinsicOutArray = timer.measure("IntrinsicBlur") { intrinsicBlur( renderscriptContext, inputArray, vectorSize, sizeX, sizeY, radius, restriction ) } val toolkitOutArray = timer.measure("ToolkitBlur") { toolkit.blur(inputArray, vectorSize, sizeX, sizeY, radius, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceBlur") { referenceBlur(inputArray, vectorSize, sizeX, sizeY, radius, restriction) } return validateSame("blur", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("blur $vectorSize ($sizeX, $sizeY) radius = $radius $restriction") logArray("blur input ", inputArray) logArray("blur reference out", referenceOutArray) logArray("blur intrinsic out", intrinsicOutArray) logArray("blur toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testOneBitmapBlur( timer: TimingTracker, bitmap: Bitmap, radius: Int, restriction: Range2d? ): Boolean { val intrinsicOutArray = timer.measure("IntrinsicBlur") { intrinsicBlur(renderscriptContext, bitmap, radius, restriction) } val toolkitOutBitmap = timer.measure("ToolkitBlur") { toolkit.blur(bitmap, radius, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceBlur") { referenceBlur( getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height, radius, restriction ) } val toolkitOutArray = getBitmapBytes(toolkitOutBitmap) return validateSame("blur", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("BlurBitmap ${bitmap.config} $radius $restriction") logArray("blur reference out", referenceOutArray) logArray("blur intrinsic out", intrinsicOutArray) logArray("blur toolkit out", toolkitOutArray) } } enum class ColorMatrixConversionType { RGB_TO_YUV, YUV_TO_RGB, GREYSCALE, RANDOM } @ExperimentalUnsignedTypes private fun testColorMatrix(timer: TimingTracker): Boolean { return ColorMatrixConversionType.values().all { conversion -> testOneBitmapColorMatrix(timer, testImage1, conversion, null) and testOneBitmapColorMatrix( timer, testImage1, conversion, Range2d(6, 23, 2, 4) ) and commonLayoutsToTry.all { (sizeX, sizeY, restriction) -> (1..4).all { inputVectorSize -> (1..4).all { outputVectorSize -> testOneRandomColorMatrix( timer, inputVectorSize, sizeX, sizeY, outputVectorSize, conversion, restriction ) } } } } } @ExperimentalUnsignedTypes private fun testOneRandomColorMatrix( timer: TimingTracker, inputVectorSize: Int, sizeX: Int, sizeY: Int, outputVectorSize: Int, conversion: ColorMatrixConversionType, restriction: Range2d? ): Boolean { val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(inputVectorSize)) val addVector = randomFloatArray(0x243238, 4, 1, 1, 0.3f) val matrix = when (conversion) { ColorMatrixConversionType.RGB_TO_YUV -> toolkit.rgbToYuvMatrix ColorMatrixConversionType.YUV_TO_RGB -> toolkit.yuvToRgbMatrix ColorMatrixConversionType.GREYSCALE -> toolkit.greyScaleColorMatrix ColorMatrixConversionType.RANDOM -> randomFloatArray(0x234348, 4, 4, 1) } val intrinsicOutArray = timer.measure("IntrinsicColorMatrix") { intrinsicColorMatrix( renderscriptContext, conversion, inputArray, inputVectorSize, sizeX, sizeY, outputVectorSize, matrix, addVector, restriction ) } val toolkitOutArray = timer.measure("ToolkitColorMatrix") { toolkit.colorMatrix( inputArray, inputVectorSize, sizeX, sizeY, outputVectorSize, matrix, addVector, restriction ) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceColorMatrix") { referenceColorMatrix( inputArray, inputVectorSize, sizeX, sizeY, outputVectorSize, matrix, addVector, restriction ) } return validateSame("colorMatrix", intrinsicOutArray, referenceOutArray, toolkitOutArray, outputVectorSize == 3) { println("colorMatrix ($sizeX, $sizeY) $inputVectorSize->$outputVectorSize $restriction") logArray("colorMatrix matrix ", matrix, 16) logArray("colorMatrix addVector", addVector, 4) logArray("colorMatrix in ", inputArray) logArray("colorMatrix reference out", referenceOutArray, 300) logArray("colorMatrix intrinsic out", intrinsicOutArray, 300) logArray("colorMatrix toolkit out", toolkitOutArray, 300) } } @ExperimentalUnsignedTypes private fun testOneBitmapColorMatrix( timer: TimingTracker, bitmap: Bitmap, conversion: ColorMatrixConversionType, restriction: Range2d? ): Boolean { val addVector = randomFloatArray(0x243238, 4, 1, 1, 0.3f) val matrix = when (conversion) { ColorMatrixConversionType.RGB_TO_YUV -> toolkit.rgbToYuvMatrix ColorMatrixConversionType.YUV_TO_RGB -> toolkit.yuvToRgbMatrix ColorMatrixConversionType.GREYSCALE -> toolkit.greyScaleColorMatrix ColorMatrixConversionType.RANDOM -> randomFloatArray(0x234348, 4, 4, 1) } val intrinsicOutArray = timer.measure("IntrinsicColorMatrix") { intrinsicColorMatrix( renderscriptContext, conversion, bitmap, matrix, addVector, restriction ) } val toolkitOutBitmap = timer.measure("ToolkitColorMatrix") { toolkit.colorMatrix(bitmap, matrix, addVector, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceColorMatrix") { referenceColorMatrix( getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height, vectorSizeOfBitmap(bitmap), matrix, addVector, restriction ) } val toolkitOutArray = getBitmapBytes(toolkitOutBitmap) return validateSame("ColorMatrix", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("colorMatrixBitmap $restriction") logArray("colorMatrixBitmap matrix ", matrix, 16) logArray("colorMatrixBitmap addVector", addVector, 4) logArray("colorMatrixBitmap reference out", referenceOutArray) logArray("colorMatrixBitmap intrinsic out", intrinsicOutArray) logArray("colorMatrixBitmap toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testConvolve(timer: TimingTracker): Boolean { val coefficientsToTry = listOf( randomFloatArray(0x2937021, 3, 3, 1, 0.1f), randomFloatArray(0x2937021, 5, 5, 1, 0.05f) ) return coefficientsToTry.all { coefficients -> testOneBitmapConvolve(timer, testImage1, coefficients, null) and testOneBitmapConvolve(timer, testImage1, coefficients, Range2d(6, 23, 2, 4)) and commonLayoutsToTry.all { (sizeX, sizeY, restriction) -> (1..4).all { vectorSize -> testOneRandomConvolve( timer, vectorSize, sizeX, sizeY, coefficients, restriction ) } } } } @ExperimentalUnsignedTypes private fun testOneRandomConvolve( timer: TimingTracker, vectorSize: Int, sizeX: Int, sizeY: Int, coefficients: FloatArray, restriction: Range2d? ): Boolean { val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize)) val intrinsicOutArray = timer.measure("IntrinsicConvolve") { intrinsicConvolve( renderscriptContext, inputArray, vectorSize, sizeX, sizeY, coefficients, restriction ) } val toolkitOutArray = timer.measure("ToolkitConvolve") { toolkit.convolve(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceConvolve") { referenceConvolve(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction) } val task = if (coefficients.size == 9) "convolve3x3 $vectorSize" else "convolve5x5 $vectorSize" return validateSame(task, intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("Convolve $vectorSize ($sizeX, $sizeY) $restriction") logArray("Convolve coefficients", coefficients, 25) logArray("Convolve in ", inputArray) logArray("Convolve reference out", referenceOutArray) logArray("Convolve intrinsic out", intrinsicOutArray) logArray("Convolve toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testOneBitmapConvolve( timer: TimingTracker, bitmap: Bitmap, coefficients: FloatArray, restriction: Range2d? ): Boolean { val intrinsicOutArray = timer.measure("IntrinsicConvolve") { intrinsicConvolve(renderscriptContext, bitmap, coefficients, restriction) } val toolkitOutBitmap = timer.measure("ToolkitConvolve") { toolkit.convolve(bitmap, coefficients, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceConvolve") { referenceConvolve( getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height, coefficients, restriction ) } val task = if (coefficients.size == 9) "convolve3x3" else "convolve5x5" val toolkitOutArray = getBitmapBytes(toolkitOutBitmap) return validateSame(task, intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("ConvolveBitmap $restriction") logArray("ConvolveBitmap coefficients", coefficients, 25) //logArray("ConvolveBitmap in ", inputArray) logArray("ConvolveBitmap reference out", referenceOutArray) logArray("ConvolveBitmap intrinsic out", intrinsicOutArray) logArray("ConvolveBitmap toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testHistogram(timer: TimingTracker): Boolean { val coefficients = floatArrayOf(0.1f, 0.3f, 0.5f, 0.05f) return testOneBitmapHistogram(timer, testImage1, null) and testOneBitmapHistogram(timer, testImage1, Range2d(6, 23, 2, 4)) and testOneBitmapHistogramDot(timer, testImage1, null, null) and testOneBitmapHistogramDot(timer, testImage1, coefficients, null) and testOneBitmapHistogramDot(timer, testImage1, coefficients, Range2d(6, 23, 2, 4)) and commonLayoutsToTry.all { (sizeX, sizeY, restriction) -> (1..4).all { vectorSize -> testOneRandomHistogram(timer, vectorSize, sizeX, sizeY, restriction) && testOneRandomHistogramDot( timer, vectorSize, sizeX, sizeY, null, restriction ) && testOneRandomHistogramDot( timer, vectorSize, sizeX, sizeY, coefficients.sliceArray(0 until vectorSize), restriction ) } } } @ExperimentalUnsignedTypes private fun testOneRandomHistogram( timer: TimingTracker, vectorSize: Int, sizeX: Int, sizeY: Int, restriction: Range2d? ): Boolean { val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize)) val intrinsicOutput = timer.measure("IntrinsicHistogram") { intrinsicHistogram( renderscriptContext, inputArray, vectorSize, sizeX, sizeY, restriction ) } val toolkitOutput = timer.measure("ToolkitHistogram") { toolkit.histogram(inputArray, vectorSize, sizeX, sizeY, restriction) } if (!validate) return true val referenceOutput = timer.measure("ReferenceHistogram") { referenceHistogram( inputArray, vectorSize, sizeX, sizeY, restriction ) } return validateSame("histogram", intrinsicOutput, referenceOutput, toolkitOutput, 0) { println("histogram $vectorSize ($sizeX, $sizeY) $restriction") logArray("histogram in ", inputArray, 200) logArray("histogram reference out", referenceOutput, 200) logArray("histogram intrinsic out", intrinsicOutput, 200) logArray("histogram toolkit out", toolkitOutput, 200) } } @ExperimentalUnsignedTypes private fun testOneBitmapHistogram( timer: TimingTracker, bitmap: Bitmap, restriction: Range2d? ): Boolean { val intrinsicOutput = timer.measure("IntrinsicHistogram") { intrinsicHistogram(renderscriptContext, bitmap, restriction) } val toolkitOutput = timer.measure("ToolkitHistogram") { toolkit.histogram(bitmap, restriction) } if (!validate) return true val referenceOutput = timer.measure("ReferenceHistogram") { referenceHistogram( getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height, restriction ) } return validateSame("histogram", intrinsicOutput, referenceOutput, toolkitOutput, 0) { println("HistogramBitmap $restriction") logArray("HistogramBitmap reference out", referenceOutput) logArray("HistogramBitmap intrinsic out", intrinsicOutput) logArray("HistogramBitmap toolkit out", toolkitOutput) } } @ExperimentalUnsignedTypes private fun testOneRandomHistogramDot( timer: TimingTracker, vectorSize: Int, sizeX: Int, sizeY: Int, coefficients: FloatArray?, restriction: Range2d? ): Boolean { val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, paddedSize(vectorSize)) val intrinsicOutArray = timer.measure("IntrinsicHistogramDot") { intrinsicHistogramDot( renderscriptContext, inputArray, vectorSize, sizeX, sizeY, coefficients, restriction ) } val toolkitOutArray = timer.measure("ToolkitHistogramDot") { toolkit.histogramDot( inputArray, vectorSize, sizeX, sizeY, coefficients, restriction ) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceHistogramDot") { referenceHistogramDot(inputArray, vectorSize, sizeX, sizeY, coefficients, restriction) } return validateSame("histogramDot", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("histogramDot $vectorSize ($sizeX, $sizeY) $restriction") logArray("histogramDot coefficients ", coefficients) logArray("histogramDot in ", inputArray) logArray("histogramDot reference out", referenceOutArray, 256) logArray("histogramDot intrinsic out", intrinsicOutArray, 256) logArray("histogramDot toolkit out", toolkitOutArray, 256) } } @ExperimentalUnsignedTypes private fun testOneBitmapHistogramDot( timer: TimingTracker, bitmap: Bitmap, coefficients: FloatArray?, restriction: Range2d? ): Boolean { val intrinsicOutArray = timer.measure("IntrinsicHistogramDot") { intrinsicHistogramDot(renderscriptContext, bitmap, coefficients, restriction) } val toolkitOutArray = timer.measure("ToolkitHistogramDot") { toolkit.histogramDot(bitmap, coefficients, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceHistogramDot") { referenceHistogramDot( getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height, coefficients, restriction ) } return validateSame( "HistogramDotBitmap", intrinsicOutArray, referenceOutArray, toolkitOutArray ) { println("HistogramDotBitmap $restriction") logArray("HistogramDotBitmap coefficients ", coefficients) //logArray("HistogramDotBitmap in ", inputArray) logArray("HistogramDotBitmap reference out", referenceOutArray, 256) logArray("HistogramDotBitmap intrinsic out", intrinsicOutArray, 256) logArray("HistogramDotBitmap toolkit out", toolkitOutArray, 256) } } @ExperimentalUnsignedTypes private fun testLut(timer: TimingTracker): Boolean { return testOneBitmapLut(timer, testImage1, null) and testOneBitmapLut(timer, testImage1, Range2d(6, 23, 2, 4)) and commonLayoutsToTry.all { (sizeX, sizeY, restriction) -> testOneRandomLut(timer, sizeX, sizeY, restriction) } } @ExperimentalUnsignedTypes private fun testOneRandomLut( timer: TimingTracker, sizeX: Int, sizeY: Int, restriction: Range2d? ): Boolean { val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, 4) val newRed = randomByteArray(0x32425, 256, 1, 1) val newGreen = randomByteArray(0x1F3225, 256, 1, 1) val newBlue = randomByteArray(0x32D4F27, 256, 1, 1) val newAlpha = randomByteArray(0x3A20001, 256, 1, 1) val table = LookupTable() table.red = newRed table.blue = newBlue table.green = newGreen table.alpha = newAlpha val intrinsicOutArray = timer.measure("IntrinsicLUT") { intrinsicLut( renderscriptContext, inputArray, sizeX, sizeY, newRed, newGreen, newBlue, newAlpha, restriction ) } val toolkitOutArray = timer.measure("ToolkitLUT") { toolkit.lut(inputArray, sizeX, sizeY, table, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceLUT") { referenceLut(inputArray, sizeX, sizeY, table, restriction) } return validateSame("LUT", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("lut ($sizeX, $sizeY) $restriction") logArray("LUT red ", newRed, 256) logArray("LUT green", newGreen, 256) logArray("LUT blue ", newBlue, 256) logArray("LUT alpha", newAlpha, 256) logArray("LUT in ", inputArray) logArray("LUT reference out", referenceOutArray) logArray("LUT intrinsic out", intrinsicOutArray) logArray("LUT toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testOneBitmapLut( timer: TimingTracker, bitmap: Bitmap, restriction: Range2d? ): Boolean { val newRed = randomByteArray(0x32425, 256, 1, 1) val newGreen = randomByteArray(0x1F3225, 256, 1, 1) val newBlue = randomByteArray(0x32D4F27, 256, 1, 1) val newAlpha = randomByteArray(0x3A20001, 256, 1, 1) val table = LookupTable() table.red = newRed table.blue = newBlue table.green = newGreen table.alpha = newAlpha val intrinsicOutArray = timer.measure("IntrinsicLUT") { intrinsicLut( renderscriptContext, bitmap, newRed, newGreen, newBlue, newAlpha, restriction ) } val toolkitOutBitmap = timer.measure("ToolkitLUT") { toolkit.lut(bitmap, table, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceLUT") { referenceLut( getBitmapBytes(bitmap), bitmap.width, bitmap.height, table, restriction ) } val toolkitOutArray = getBitmapBytes(toolkitOutBitmap) return validateSame("LutBitmap", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("LutBitmap $restriction") logArray("LutBitmap red ", newRed, 256) logArray("LutBitmap green", newGreen, 256) logArray("LutBitmap blue ", newBlue, 256) logArray("LutBitmap alpha", newAlpha, 256) //logArray("LutBitmap in ", inputArray, 80) logArray("LutBitmap reference out", referenceOutArray) logArray("LutBitmap intrinsic out", intrinsicOutArray) logArray("LutBitmap toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testLut3d(timer: TimingTracker): Boolean { val cubeSizesToTry = listOf( Dimension(2, 2, 2), Dimension(32, 32, 16), Dimension(256, 256, 256) ) return cubeSizesToTry.all { cubeSize -> val identityCube = identityCube(cubeSize) val randomCube = randomCube(0x23424, cubeSize) testOneBitmapLut3d(timer, testImage1, cubeSize, identityCube, 1, null) and testOneBitmapLut3d(timer, testImage2, cubeSize, randomCube, 3, null) and testOneBitmapLut3d(timer, testImage2, cubeSize, randomCube, 3, Range2d(6, 23, 2, 4)) and commonLayoutsToTry.all { (sizeX, sizeY, restriction) -> testOneRandomLut3d(timer, sizeX, sizeY, cubeSize, identityCube, 1, restriction) && testOneRandomLut3d( timer, sizeX, sizeY, cubeSize, randomCube, 3, restriction ) } } } @ExperimentalUnsignedTypes private fun testOneRandomLut3d( timer: TimingTracker, sizeX: Int, sizeY: Int, cubeSize: Dimension, cubeArray: ByteArray, allowedIntError: Int, restriction: Range2d? ): Boolean { val inputArray = randomByteArray(0x50521f0, sizeX, sizeY, 4) val intrinsicOutArray = timer.measure("IntrinsicLut3d") { intrinsicLut3d( renderscriptContext, inputArray, sizeX, sizeY, cubeArray, cubeSize, restriction ) } val toolkitOutArray = timer.measure("ToolkitLut3d") { val toolkitCube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ) toolkit.lut3d(inputArray, sizeX, sizeY, toolkitCube, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceLut3d") { val cube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ) referenceLut3d(inputArray, sizeX, sizeY, cube, restriction) } return validateSame( "lut3d", intrinsicOutArray, referenceOutArray, toolkitOutArray, false, allowedIntError ) { println("lut3d ($sizeX, $sizeY) $restriction") logArray("lut3d cube", cubeArray, 256) logArray("lut3d in ", inputArray, 64) logArray("lut3d reference out", referenceOutArray, 64) logArray("lut3d intrinsic out", intrinsicOutArray, 64) logArray("lut3d toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testOneBitmapLut3d( timer: TimingTracker, bitmap: Bitmap, cubeSize: Dimension, cubeArray: ByteArray, allowedIntError: Int, restriction: Range2d? ): Boolean { val intrinsicOutArray = timer.measure("IntrinsicLut3d") { intrinsicLut3d(renderscriptContext, bitmap, cubeArray, cubeSize, restriction) } val toolkitOutBitmap = timer.measure("ToolkitLut3d") { val toolkitCube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ) toolkit.lut3d(bitmap, toolkitCube, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceLut3d") { val cube = Rgba3dArray(cubeArray, cubeSize.sizeX, cubeSize.sizeY, cubeSize.sizeZ) referenceLut3d(getBitmapBytes(bitmap), bitmap.width, bitmap.height, cube, restriction) } val toolkitOutArray = getBitmapBytes(toolkitOutBitmap) return validateSame( "Lut3dBitmap", intrinsicOutArray, referenceOutArray, toolkitOutArray, false, allowedIntError ) { println("Lut3dBitmap $restriction") logArray("Lut3dBitmap cube", cubeArray, 256) //logArray("Lut3dBitmap in ", inputArray, 64) logArray("Lut3dBitmap reference out", referenceOutArray, 64) logArray("Lut3dBitmap intrinsic out", intrinsicOutArray, 64) logArray("Lut3dBitmap toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testResize(timer: TimingTracker): Boolean { val factorsToTry = listOf( Pair(1f, 1f), Pair(0.5f, 1f), Pair(2f, 2f), Pair(0.5f, 2f), Pair(2f, 0.5f), // The RenderScript Intrinsic tests used the above factors. It's tempting to use // less regular ones like Pair(6.37f, 0.17f) however this creates small offset // errors between the result provided by the C++ code and the SIMD code. This is // due to the SIMD code using a scaled integer to increment going from one pixel to the // next, while the C++ code uses float operations. ) val layoutsToTry = listOf( TestLayout(37, 47, null), TestLayout(60, 10, null), TestLayout(6, 4, Range2d(1, 3, 0, 2)), TestLayout(10, 14, Range2d(2, 3, 3, 7)), ) return factorsToTry.all { (scaleX, scaleY) -> // Do one resize that's greater than 4x, as that's used in the code but don't do it // for everything, as some images will get very large testOneRandomResize(timer, 1, 25, 30, 6f, 6f, null) and testOneBitmapResize(timer, testImage1, scaleX, scaleY, null) and testOneBitmapResize(timer, testImage1, scaleX, scaleY, Range2d(6, 23, 2, 4)) and layoutsToTry.all { (sizeX, sizeY, restriction) -> (1..4).all { vectorSize -> testOneRandomResize( timer, vectorSize, sizeX, sizeY, scaleX, scaleY, restriction ) } } } } @ExperimentalUnsignedTypes private fun testOneRandomResize( timer: TimingTracker, vectorSize: Int, inSizeX: Int, inSizeY: Int, scaleX: Float, scaleY: Float, restriction: Range2d? ): Boolean { val inputArray = randomByteArray(0x50521f0, inSizeX, inSizeY, paddedSize(vectorSize)) val outSizeX = (inSizeX * scaleX).toInt() val outSizeY = (inSizeY * scaleY).toInt() val intrinsicOutArray = timer.measure("IntrinsicResize") { intrinsicResize( renderscriptContext, inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY, restriction ) } val toolkitOutArray = timer.measure("ToolkitResize") { toolkit.resize( inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY, restriction ) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceResize") { referenceResize( inputArray, vectorSize, inSizeX, inSizeY, outSizeX, outSizeY, restriction ) } return validateSame("resize", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("resize $vectorSize ($inSizeX, $inSizeY) by ($scaleX, $scaleY) to ($outSizeX, $outSizeY), $restriction") logArray("resize in ", inputArray) logArray("resize reference out", referenceOutArray) logArray("resize intrinsic out", intrinsicOutArray) logArray("resize toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testOneBitmapResize( timer: TimingTracker, bitmap: Bitmap, scaleX: Float, scaleY: Float, restriction: Range2d? ): Boolean { // println("Doing resize $inSizeX x $inSizeY x $vectorSize, $scaleX x $scaleY, $restriction") val outSizeX = (bitmap.width * scaleX).toInt() val outSizeY = (bitmap.height * scaleY).toInt() val intrinsicOutArray = timer.measure("IntrinsicResize") { intrinsicResize(renderscriptContext, bitmap, outSizeX, outSizeY, restriction) } val toolkitOutBitmap = timer.measure("ToolkitResize") { toolkit.resize(bitmap, outSizeX, outSizeY, restriction) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceResize") { referenceResize( getBitmapBytes(bitmap), vectorSizeOfBitmap(bitmap), bitmap.width, bitmap.height, outSizeX, outSizeY, restriction ) } val toolkitOutArray = getBitmapBytes(toolkitOutBitmap) return validateSame("ResizeBitmap", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("ResizeBitmap by ($scaleX, $scaleY) to ($outSizeX, $outSizeY), $restriction") //logArray("ResizeBitmap in ", inputArray, 100) logArray("ResizeBitmap reference out", referenceOutArray) logArray("ResizeBitmap intrinsic out", intrinsicOutArray) logArray("ResizeBitmap toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testYuvToRgb(timer: TimingTracker): Boolean { val layoutsToTry = listOf( // Don't try sizeX with odd values. That's not allowed by definition of some // of the video formats. TestLayout(10, 14, null), TestLayout(64, 40, null), TestLayout(96, 94, null), ) return layoutsToTry.all { (sizeX, sizeY, _) -> YuvFormat.values().all { format -> testOneRandomYuvToRgb(timer, sizeX, sizeY, format) and testOneRandomYuvToRgbBitmap(timer, sizeX, sizeY, format) } } } @ExperimentalUnsignedTypes private fun testOneRandomYuvToRgb( timer: TimingTracker, sizeX: Int, sizeY: Int, format: YuvFormat ): Boolean { // The RenderScript Intrinsic does not handle this combination correctly. if (format == YuvFormat.YV12 && sizeX % 32 != 0) { return true } val inputArray = randomYuvArray(0x50521f0, sizeX, sizeY, format) val intrinsicOutArray = timer.measure("IntrinsicYuvToRgb") { intrinsicYuvToRgb(renderscriptContext, inputArray, sizeX, sizeY, format) } val toolkitOutArray = timer.measure("ToolkitYuvToRgb") { toolkit.yuvToRgb(inputArray, sizeX, sizeY, format) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceYuvToRgb") { referenceYuvToRgb(inputArray, sizeX, sizeY, format) } return validateSame("yuvToRgb", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("yuvToRgb ($sizeX, $sizeY) $format") logArray("yuvToRgb in ", inputArray) logArray("yuvToRgb reference out", referenceOutArray) logArray("yuvToRgb intrinsic out", intrinsicOutArray) logArray("yuvToRgb toolkit out", toolkitOutArray) } } @ExperimentalUnsignedTypes private fun testOneRandomYuvToRgbBitmap( timer: TimingTracker, sizeX: Int, sizeY: Int, format: YuvFormat ): Boolean { // The RenderScript Intrinsic does not handle this combination correctly. if (format == YuvFormat.YV12 && sizeX % 32 != 0) { return true } val inputArray = randomYuvArray(0x50521f0, sizeX, sizeY, format) val intrinsicOutArray = timer.measure("IntrinsicYuvToRgb") { intrinsicYuvToRgb(renderscriptContext, inputArray, sizeX, sizeY, format) } val toolkitOutBitmap = timer.measure("ToolkitYuvToRgb") { toolkit.yuvToRgbBitmap(inputArray, sizeX, sizeY, format) } if (!validate) return true val referenceOutArray = timer.measure("ReferenceYuvToRgb") { referenceYuvToRgb(inputArray, sizeX, sizeY, format) } val toolkitOutArray = getBitmapBytes(toolkitOutBitmap) return validateSame("yuvToRgb", intrinsicOutArray, referenceOutArray, toolkitOutArray) { println("yuvToRgb ($sizeX, $sizeY) $format") logArray("yuvToRgb in ", inputArray) logArray("yuvToRgb reference out", referenceOutArray) logArray("yuvToRgb intrinsic out", intrinsicOutArray) logArray("yuvToRgb toolkit out", toolkitOutArray) } } /** * Verifies that the arrays returned by the Intrinsic, the reference code, and the Toolkit * are all within a margin of error. * * RenderScript Intrinsic test (rc/android/cts/rscpp/RSCppTest.java) used 3 for ints. * For floats, rc/android/cts/rscpp/verify.rscript uses 0.0001f. */ @ExperimentalUnsignedTypes private fun validateSame( task: String, intrinsic: ByteArray, reference: ByteArray, toolkit: ByteArray, skipFourth: Boolean = false, allowedIntDelta: Int = 3, errorLogging: () -> Unit ): Boolean { val success = validateAgainstReference( task, reference, "Intrinsic", intrinsic, skipFourth, allowedIntDelta ) and validateAgainstReference( task, reference, "Toolkit", toolkit, skipFourth, allowedIntDelta ) if (!success) { println("$task FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!") errorLogging() } return success } private fun validateSame( task: String, intrinsic: IntArray, reference: IntArray, toolkit: IntArray, allowedIntDelta: Int = 3, errorLogging: () -> Unit ): Boolean { val success = validateAgainstReference( task, reference, "Intrinsic", intrinsic, allowedIntDelta ) and validateAgainstReference( task, reference, "Toolkit", toolkit, allowedIntDelta ) if (!success) { println("$task FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!FAIL!") errorLogging() } return success } @ExperimentalUnsignedTypes private fun validateAgainstReference( task: String, in1: ByteArray, name2: String, in2: ByteArray, skipFourth: Boolean, allowedIntDelta: Int ): Boolean { if (in1.size != in2.size) { println("$task. Sizes don't match: Reference ${in1.size}, $name2 ${in2.size}") return false } var same = true val maxDetails = 80 val diffs = CharArray(min(in1.size, maxDetails)) {'.'} for (i in in1.indices) { if (skipFourth && i % 4 == 3) { continue } val delta = abs(in1[i].toUByte().toInt() - in2[i].toUByte().toInt()) if (delta > allowedIntDelta) { if (same) { println( "$task. At $i, Reference is ${in1[i].toUByte()}, $name2 is ${in2[i].toUByte()}" ) } if (i < maxDetails) diffs[i] = 'X' same = false } } if (!same) { for (i in 0 until (min(in1.size, maxDetails) / 4)) print("%-3d|".format(i)) println() println(diffs) } return same } private fun validateAgainstReference( task: String, in1: IntArray, name2: String, in2: IntArray, allowedIntDelta: Int ): Boolean { if (in1.size != in2.size) { println("$task. Sizes don't match: Reference ${in1.size}, $name2 ${in2.size}") return false } for (i in in1.indices) { val delta = abs(in1[i] - in2[i]) if (delta > allowedIntDelta) { println("$task. At $i, Reference is ${in1[i]}, $name2 is ${in2[i]}") return false } } return true } }