Skip to content

Commit ad3c38e

Browse files
committed
android: [render-validation] add more test result details
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
1 parent e2b1957 commit ad3c38e

File tree

7 files changed

+157
-17
lines changed

7 files changed

+157
-17
lines changed

android/filament-utils-android/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size
6161
add_library(filament-utils-jni SHARED
6262
src/main/cpp/AutomationEngine.cpp
6363
src/main/cpp/Bookmark.cpp
64+
src/main/cpp/DeviceUtils.cpp
6465
src/main/cpp/HDRLoader.cpp
6566
src/main/cpp/IBLPrefilterContext.cpp
6667
src/main/cpp/Utils.cpp
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (C) 2026 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+
#include <jni.h>
18+
19+
#include <filament/Engine.h>
20+
21+
#include <backend/Platform.h>
22+
23+
#include <utils/CString.h>
24+
25+
using namespace filament;
26+
27+
extern "C" JNIEXPORT jstring JNICALL
28+
Java_com_google_android_filament_utils_DeviceUtils_nGetGpuDriverInfo(JNIEnv* env, jclass,
29+
jlong nativeEngine) {
30+
Engine* engine = (Engine*) nativeEngine;
31+
if (engine) {
32+
backend::Platform* platform = engine->getPlatform();
33+
backend::Driver* driver = const_cast<backend::Driver*>(engine->getDriver());
34+
if (platform) {
35+
utils::CString fullInfo;
36+
if (engine->getBackend() == backend::Backend::VULKAN) {
37+
utils::CString deviceName = platform->getDeviceInfo(
38+
backend::Platform::DeviceInfoType::VULKAN_DEVICE_NAME, driver);
39+
utils::CString driverName = platform->getDeviceInfo(
40+
backend::Platform::DeviceInfoType::VULKAN_DRIVER_NAME, driver);
41+
utils::CString driverInfo = platform->getDeviceInfo(
42+
backend::Platform::DeviceInfoType::VULKAN_DRIVER_INFO, driver);
43+
44+
if (!deviceName.empty()) fullInfo += deviceName.c_str();
45+
if (!driverName.empty()) {
46+
if (!fullInfo.empty()) fullInfo += " | ";
47+
fullInfo += driverName.c_str();
48+
}
49+
if (!driverInfo.empty()) {
50+
if (!fullInfo.empty()) fullInfo += " | ";
51+
fullInfo += driverInfo.c_str();
52+
}
53+
} else if (engine->getBackend() == backend::Backend::OPENGL) {
54+
utils::CString vendor = platform->getDeviceInfo(
55+
backend::Platform::DeviceInfoType::OPENGL_VENDOR, driver);
56+
utils::CString renderer = platform->getDeviceInfo(
57+
backend::Platform::DeviceInfoType::OPENGL_RENDERER, driver);
58+
utils::CString version = platform->getDeviceInfo(
59+
backend::Platform::DeviceInfoType::OPENGL_VERSION, driver);
60+
61+
if (!vendor.empty()) fullInfo += vendor.c_str();
62+
if (!renderer.empty()) {
63+
if (!fullInfo.empty()) fullInfo += " | ";
64+
fullInfo += renderer.c_str();
65+
}
66+
if (!version.empty()) {
67+
if (!fullInfo.empty()) fullInfo += " | ";
68+
fullInfo += version.c_str();
69+
}
70+
}
71+
if (!fullInfo.empty()) {
72+
return env->NewStringUTF(fullInfo.c_str());
73+
}
74+
}
75+
}
76+
return env->NewStringUTF("");
77+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (C) 2026 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 com.google.android.filament.utils;
18+
19+
import com.google.android.filament.Engine;
20+
21+
public class DeviceUtils {
22+
public static String getGpuDriverInfo(Engine engine) {
23+
return nGetGpuDriverInfo(engine.getNativeObject());
24+
}
25+
private static native String nGetGpuDriverInfo(long nativeEngine);
26+
}

android/samples/sample-render-validation/src/main/assets/default_test.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
"view": {
3131
"postProcessingEnabled": true,
3232
"dithering": "NONE"
33-
},
34-
"tolerance": {
35-
"maxAbsDiff": 0.1,
36-
"maxFailingPixelsFraction": 0.0
3733
}
34+
},
35+
"tolerance": {
36+
"maxAbsDiff": 0.1,
37+
"maxFailingPixelsFraction": 0.0
3838
}
3939
},
4040
{

android/samples/sample-render-validation/src/main/java/com/google/android/filament/validation/MainActivity.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,18 @@ class MainActivity : Activity(), ValidationRunner.Callback {
368368
}
369369
}
370370

371+
private fun createResultManager(outputDir: File): ValidationResultManager {
372+
val gpuDriverInfo = com.google.android.filament.utils.DeviceUtils.getGpuDriverInfo(modelViewer.engine)
373+
return ValidationResultManager(
374+
outputDir = outputDir,
375+
gpuDriverInfo = gpuDriverInfo,
376+
deviceName = android.os.Build.MODEL,
377+
deviceCodeName = android.os.Build.DEVICE,
378+
androidVersion = android.os.Build.VERSION.RELEASE,
379+
androidBuildNumber = android.os.Build.DISPLAY
380+
)
381+
}
382+
371383
private fun startValidation(input: ValidationInputManager.ValidationInput) {
372384
try {
373385
resultsContainer.removeAllViews()
@@ -378,7 +390,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
378390

379391
testResultsHeader.text = "${input.config.name}"
380392

381-
resultManager = ValidationResultManager(input.outputDir)
393+
resultManager = createResultManager(input.outputDir)
382394

383395
validationRunner = ValidationRunner(this, modelViewer, input.config, resultManager!!)
384396
validationRunner?.callback = this
@@ -534,7 +546,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
534546
private fun exportTestBundleAction() {
535547
currentInput?.let { input ->
536548
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
537-
val rm = resultManager ?: ValidationResultManager(input.outputDir)
549+
val rm = resultManager ?: createResultManager(input.outputDir)
538550
val zip = rm.exportTestBundle(input.config, timestamp)
539551
if (zip != null) {
540552
val msg = "Exported Bundle: ${zip.name}"
@@ -550,7 +562,7 @@ class MainActivity : Activity(), ValidationRunner.Callback {
550562
private fun exportTestResultsAction() {
551563
currentInput?.let { input ->
552564
val timestamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
553-
val rm = resultManager ?: ValidationResultManager(input.outputDir)
565+
val rm = resultManager ?: createResultManager(input.outputDir)
554566
val zip = rm.exportTestResults(input.sourceZip, timestamp)
555567
if (zip != null) {
556568
val msg = "Exported Results: ${zip.name}"

android/samples/sample-render-validation/src/main/java/com/google/android/filament/validation/ValidationResultManager.kt

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,14 @@ data class ValidationResult(
3131
val diffMetric: Float = 0f
3232
)
3333

34-
class ValidationResultManager(private val outputDir: File) {
34+
class ValidationResultManager(
35+
private val outputDir: File,
36+
private val gpuDriverInfo: String,
37+
private val deviceName: String,
38+
private val deviceCodeName: String,
39+
private val androidVersion: String,
40+
private val androidBuildNumber: String
41+
) {
3542

3643
companion object {
3744
private const val TAG = "ValidationResultManager"
@@ -64,9 +71,9 @@ class ValidationResultManager(private val outputDir: File) {
6471
return outputDir
6572
}
6673

67-
fun finalizeResults(): File? {
74+
fun finalizeResults(totalTimeMs: Long): File? {
6875
// Write results JSON
69-
writeResultsJson()
76+
writeResultsJson(totalTimeMs)
7077
return null
7178
}
7279

@@ -103,10 +110,10 @@ class ValidationResultManager(private val outputDir: File) {
103110
zos.closeEntry()
104111
}
105112

106-
// 3. Add diff images (any file ending in _diff.png in outputDir)
107-
outputDir.listFiles { _, name -> name.endsWith("_diff.png") }?.forEach { diffFile ->
108-
zos.putNextEntry(ZipEntry(diffFile.name))
109-
diffFile.inputStream().use { it.copyTo(zos) }
113+
// 3. Add images (only rendered images, exclude diffs)
114+
outputDir.listFiles { _, name -> name.endsWith(".png") && !name.endsWith("_diff.png") }?.forEach { imgFile ->
115+
zos.putNextEntry(ZipEntry(imgFile.name))
116+
imgFile.inputStream().use { it.copyTo(zos) }
110117
zos.closeEntry()
111118
}
112119
}
@@ -266,7 +273,18 @@ class ValidationResultManager(private val outputDir: File) {
266273
}
267274
}
268275

269-
private fun writeResultsJson() {
276+
private fun writeResultsJson(totalTimeMs: Long) {
277+
val rootObject = JSONObject()
278+
279+
val metadataObject = JSONObject()
280+
metadataObject.put("gpu_driver_info", gpuDriverInfo ?: "")
281+
metadataObject.put("total_time_ms", totalTimeMs)
282+
metadataObject.put("device_name", deviceName ?: "")
283+
metadataObject.put("device_code_name", deviceCodeName ?: "")
284+
metadataObject.put("android_version", androidVersion ?: "")
285+
metadataObject.put("android_build_number", androidBuildNumber ?: "")
286+
rootObject.put("metadata", metadataObject)
287+
270288
val jsonArray = JSONArray()
271289
for (result in results) {
272290
val jsonObject = JSONObject()
@@ -275,11 +293,12 @@ class ValidationResultManager(private val outputDir: File) {
275293
jsonObject.put("diff_metric", result.diffMetric)
276294
jsonArray.put(jsonObject)
277295
}
296+
rootObject.put("results", jsonArray)
278297

279298
val jsonFile = File(outputDir, "results.json")
280299
try {
281300
FileOutputStream(jsonFile).use { out ->
282-
out.write(jsonArray.toString(4).toByteArray())
301+
out.write(rootObject.toString(4).toByteArray())
283302
}
284303
} catch (e: Exception) {
285304
Log.e(TAG, "Failed to write results.json", e)

android/samples/sample-render-validation/src/main/java/com/google/android/filament/validation/ValidationRunner.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class ValidationRunner(
4242
private var currentModelName: String? = null
4343

4444
private var frameCounter = 0
45+
private var suiteStartTime: Long = 0
4546

4647
enum class State {
4748
IDLE,
@@ -65,6 +66,7 @@ class ValidationRunner(
6566
callback?.onAllTestsFinished()
6667
return
6768
}
69+
suiteStartTime = System.currentTimeMillis()
6870
currentTestIndex = 0
6971
currentModelIndex = 0
7072
startTest(config.tests[0])
@@ -345,7 +347,10 @@ class ValidationRunner(
345347
startTest(config.tests[currentTestIndex])
346348
} else {
347349
currentState = State.IDLE
348-
resultManager.finalizeResults()
350+
351+
val totalTimeMs = System.currentTimeMillis() - suiteStartTime
352+
353+
resultManager.finalizeResults(totalTimeMs)
349354
callback?.onAllTestsFinished()
350355
}
351356
}

0 commit comments

Comments
 (0)