diff options
Diffstat (limited to 'test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt')
-rw-r--r-- | test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt | 1244 |
1 files changed, 1244 insertions, 0 deletions
diff --git a/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt b/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt new file mode 100644 index 0000000..934456f --- /dev/null +++ b/test-app/src/main/java/com/google/android/renderscript_test/AllTests.kt @@ -0,0 +1,1244 @@ +/* + * 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. + */ + +package com.google.android.renderscript_test + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.renderscript.RenderScript +import com.google.android.renderscript.BlendingMode +import com.google.android.renderscript.LookupTable +import com.google.android.renderscript.Range2d +import com.google.android.renderscript.Rgba3dArray +import com.google.android.renderscript.Toolkit +import com.google.android.renderscript.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)), + // The size of most of the original RenderScript Intrinsic tests + TestLayout(160, 100, null), + /* Other tests, if you're patient: + TestLayout(10, 14, null), + TestLayout(10, 14, Range2d(2, 3, 8, 14)), + TestLayout(125, 227, Range2d(50, 125, 100, 227)), + TestLayout(800, 600, null), + // Weirdly shaped ones + TestLayout(1, 1, null), // A single item + estLayout(16000, 1, null), // A single item + TestLayout(1, 16000, null), // One large row + // A very large test + TestLayout(1024, 2048, null), + */ +) + +enum class Intrinsic { + BLEND, + BLUR, + COLOR_MATRIX, + CONVOLVE, + HISTOGRAM, + LUT, + LUT3D, + RESIZE, + YUV_TO_RGB, +} + + +class Tester(context: Context, private val validate: Boolean) { + private val renderscriptContext = RenderScript.create(context) + 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() + } + + // Test one single intrinsic. Return true on success and false on failure. + @ExperimentalUnsignedTypes + fun testOne(intrinsic: Intrinsic, timer: TimingTracker) = + when(intrinsic) { + Intrinsic.BLEND -> ::testBlend + Intrinsic.BLUR -> ::testBlur + Intrinsic.COLOR_MATRIX -> ::testColorMatrix + Intrinsic.CONVOLVE -> ::testConvolve + Intrinsic.HISTOGRAM -> ::testHistogram + Intrinsic.LUT -> ::testLut + Intrinsic.LUT3D -> ::testLut3d + Intrinsic.RESIZE -> ::testResize + Intrinsic.YUV_TO_RGB -> ::testYuvToRgb + }.let { test -> test(timer) } + + @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 + } +} |