android: [render-validation] add more test result details (#9812)

Add the following information:
 - Android build fingerprint, version
 - GPU driver name, info, vendor name
 - Time elapsed for test
 - Rendered images (as oppose to diff image)

filament-utils:
 - Add DeviceUtils to hook into Platform methods for reading out
   strings about gpu vendor, driver.

Fix "tolerance" in test definition
This commit is contained in:
Powei Feng
2026-03-19 16:03:00 -07:00
committed by GitHub
parent 81961f6d49
commit d65589bf77
7 changed files with 165 additions and 17 deletions

View File

@@ -30,11 +30,11 @@
"view": {
"postProcessingEnabled": true,
"dithering": "NONE"
},
"tolerance": {
"maxAbsDiff": 0.1,
"maxFailingPixelsFraction": 0.0
}
},
"tolerance": {
"maxAbsDiff": 0.1,
"maxFailingPixelsFraction": 0.0
}
},
{

View File

@@ -368,6 +368,18 @@ class MainActivity : Activity(), ValidationRunner.Callback {
}
}
private fun createResultManager(outputDir: File): ValidationResultManager {
val gpuDriverInfo = com.google.android.filament.utils.DeviceUtils.getGpuDriverInfo(modelViewer.engine)
return ValidationResultManager(
outputDir = outputDir,
gpuDriverInfo = gpuDriverInfo,
deviceName = android.os.Build.MODEL,
deviceCodeName = android.os.Build.DEVICE,
androidVersion = android.os.Build.VERSION.RELEASE,
androidBuildNumber = android.os.Build.DISPLAY
)
}
private fun startValidation(input: ValidationInputManager.ValidationInput) {
try {
resultsContainer.removeAllViews()
@@ -378,7 +390,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
testResultsHeader.text = "${input.config.name}"
resultManager = ValidationResultManager(input.outputDir)
resultManager = createResultManager(input.outputDir)
validationRunner = ValidationRunner(this, modelViewer, input.config, resultManager!!)
validationRunner?.callback = this
@@ -534,7 +546,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
private fun exportTestBundleAction() {
currentInput?.let { input ->
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val rm = resultManager ?: ValidationResultManager(input.outputDir)
val rm = resultManager ?: createResultManager(input.outputDir)
val zip = rm.exportTestBundle(input.config, timestamp)
if (zip != null) {
val msg = "Exported Bundle: ${zip.name}"
@@ -550,7 +562,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
private fun exportTestResultsAction() {
currentInput?.let { input ->
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val rm = resultManager ?: ValidationResultManager(input.outputDir)
val rm = resultManager ?: createResultManager(input.outputDir)
val zip = rm.exportTestResults(input.sourceZip, timestamp)
if (zip != null) {
val msg = "Exported Results: ${zip.name}"

View File

@@ -31,7 +31,14 @@ data class ValidationResult(
val diffMetric: Float = 0f
)
class ValidationResultManager(private val outputDir: File) {
class ValidationResultManager(
private val outputDir: File,
private val gpuDriverInfo: String,
private val deviceName: String,
private val deviceCodeName: String,
private val androidVersion: String,
private val androidBuildNumber: String
) {
companion object {
private const val TAG = "ValidationResultManager"
@@ -64,9 +71,9 @@ class ValidationResultManager(private val outputDir: File) {
return outputDir
}
fun finalizeResults(): File? {
fun finalizeResults(totalTimeMs: Long): File? {
// Write results JSON
writeResultsJson()
writeResultsJson(totalTimeMs)
return null
}
@@ -103,10 +110,10 @@ class ValidationResultManager(private val outputDir: File) {
zos.closeEntry()
}
// 3. Add diff images (any file ending in _diff.png in outputDir)
outputDir.listFiles { _, name -> name.endsWith("_diff.png") }?.forEach { diffFile ->
zos.putNextEntry(ZipEntry(diffFile.name))
diffFile.inputStream().use { it.copyTo(zos) }
// 3. Add images (only rendered images, exclude diffs)
outputDir.listFiles { _, name -> name.endsWith(".png") && !name.endsWith("_diff.png") }?.forEach { imgFile ->
zos.putNextEntry(ZipEntry(imgFile.name))
imgFile.inputStream().use { it.copyTo(zos) }
zos.closeEntry()
}
}
@@ -266,7 +273,18 @@ class ValidationResultManager(private val outputDir: File) {
}
}
private fun writeResultsJson() {
private fun writeResultsJson(totalTimeMs: Long) {
val rootObject = JSONObject()
val metadataObject = JSONObject()
metadataObject.put("gpu_driver_info", gpuDriverInfo ?: "")
metadataObject.put("total_time_ms", totalTimeMs)
metadataObject.put("device_name", deviceName ?: "")
metadataObject.put("device_code_name", deviceCodeName ?: "")
metadataObject.put("android_version", androidVersion ?: "")
metadataObject.put("android_build_number", androidBuildNumber ?: "")
rootObject.put("metadata", metadataObject)
val jsonArray = JSONArray()
for (result in results) {
val jsonObject = JSONObject()
@@ -275,11 +293,12 @@ class ValidationResultManager(private val outputDir: File) {
jsonObject.put("diff_metric", result.diffMetric)
jsonArray.put(jsonObject)
}
rootObject.put("results", jsonArray)
val jsonFile = File(outputDir, "results.json")
try {
FileOutputStream(jsonFile).use { out ->
out.write(jsonArray.toString(4).toByteArray())
out.write(rootObject.toString(4).toByteArray())
}
} catch (e: Exception) {
Log.e(TAG, "Failed to write results.json", e)

View File

@@ -42,6 +42,7 @@ class ValidationRunner(
private var currentModelName: String? = null
private var frameCounter = 0
private var suiteStartTime: Long = 0
enum class State {
IDLE,
@@ -65,6 +66,7 @@ class ValidationRunner(
callback?.onAllTestsFinished()
return
}
suiteStartTime = System.currentTimeMillis()
currentTestIndex = 0
currentModelIndex = 0
startTest(config.tests[0])
@@ -356,7 +358,10 @@ class ValidationRunner(
startTest(config.tests[currentTestIndex])
} else {
currentState = State.IDLE
resultManager.finalizeResults()
val totalTimeMs = System.currentTimeMillis() - suiteStartTime
resultManager.finalizeResults(totalTimeMs)
callback?.onAllTestsFinished()
}
}