[WebGPU] Add AndroidX emulator test skips and fix annotations - Skips hardware buffer and sync fence tests on the emulator. - Adds SdkSuppress annotations up to maxSdkVersion 36. - Adds ExperimentalPropertyAnnotation to generated Kotlin properties. - Cleans up redundant @RequiresApi tags. Bug: b/549538388 Change-Id: Id378e6ae7e0aa36484e64973a727b90e8436ece3 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/333575 Reviewed-by: Tarun Saini <sainitarun@google.com> Commit-Queue: Mridul Goyal <mridulgoyal@google.com>
diff --git a/generator/templates/art/api_kotlin_types.kt b/generator/templates/art/api_kotlin_types.kt index 0e52d8a..8862736 100644 --- a/generator/templates/art/api_kotlin_types.kt +++ b/generator/templates/art/api_kotlin_types.kt
@@ -325,18 +325,18 @@ {%- endif -%} {%- set optin = kotlin_member_optin(member, parent=parent, chain_children=chain_children) | trim -%} {%- if optin -%} - {%- do annotations.append(optin) -%} + {%- do annotations.append('@android.annotation.SuppressLint("ExperimentalPropertyAnnotation")\n ' + optin) -%} {%- endif -%} - {%- set annotations_str = annotations | join(' ') -%} + {%- set annotations_str = annotations | join('\n ') -%} {%- set is_experimental = (item_is_experimental(parent) != 'True') and (item_requires_optin(member) == 'True') -%} - {{- annotations_str + ' ' if annotations_str -}}public var {{ member.name.camelCase() -}}: {{ kotlin_definition(member) if is_experimental else kotlin_declaration(member) -}} + {{- annotations_str + '\n ' if annotations_str -}}public var {{ member.name.camelCase() -}}: {{ kotlin_definition(member) if is_experimental else kotlin_declaration(member) -}} {%- endmacro -%} //* Generates a public var property declaration for an extensible chain child structure. {% macro kotlin_chain_property(child, parent) -%} {%- set optin = kotlin_member_optin(child, parent=parent, chain_children=chain_children) | trim -%} {%- set is_experimental = (item_is_experimental(parent) != 'True') and (item_requires_optin(child) == 'True') -%} - {{- optin + ' ' if optin -}}public var {{ child.name.camelCase() }}: {{ kotlin_name(child) }}?{{ ' = null' if is_experimental }} + {{- '@android.annotation.SuppressLint("ExperimentalPropertyAnnotation")\n ' + optin + '\n ' if optin -}}public var {{ child.name.camelCase() }}: {{ kotlin_name(child) }}?{{ ' = null' if is_experimental }} {%- endmacro -%}
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/AdapterTest.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/AdapterTest.kt index 27903c5..13813fd 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/AdapterTest.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/AdapterTest.kt
@@ -16,6 +16,7 @@ package androidx.webgpu import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SdkSuppress import androidx.test.filters.SmallTest import androidx.webgpu.WebGpuTestConstants.EMULATOR_TESTS_MIN_API_LEVEL import androidx.webgpu.helper.initLibrary @@ -67,6 +68,7 @@ @Test @ApiRequirement(minApi = EMULATOR_TESTS_MIN_API_LEVEL, onlySkipOnEmulator = true) + @SdkSuppress(maxSdkVersion = 36) // b/537525245 fun adapterBackendTest() { val adapterInfo = adapter.getInfo() assertEquals(
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/BufferTest.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/BufferTest.kt index 7192c08..91c6d8e 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/BufferTest.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/BufferTest.kt
@@ -16,6 +16,7 @@ package androidx.webgpu import androidx.test.filters.MediumTest +import androidx.test.filters.SdkSuppress import androidx.test.filters.SmallTest import androidx.webgpu.WebGpuTestConstants.EMULATOR_TESTS_MIN_API_LEVEL import androidx.webgpu.helper.WebGpu @@ -167,6 +168,7 @@ */ @MediumTest @Test + @SdkSuppress(maxSdkVersion = 36) // b/537525245 @ApiRequirement(minApi = EMULATOR_TESTS_MIN_API_LEVEL, onlySkipOnEmulator = true) fun testWriteAndReadBuffer() { runBlocking {
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/CommandEncoderTest.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/CommandEncoderTest.kt index a2ad283..534dc87 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/CommandEncoderTest.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/CommandEncoderTest.kt
@@ -15,6 +15,7 @@ */ package androidx.webgpu +import androidx.test.filters.SdkSuppress import androidx.test.filters.SmallTest import androidx.webgpu.WebGpuTestConstants.EMULATOR_TESTS_MIN_API_LEVEL import androidx.webgpu.helper.WebGpu @@ -193,6 +194,7 @@ * Verifies that a render pass with `LoadOp.Clear` correctly clears a texture to a specific color. */ @Test + @SdkSuppress(maxSdkVersion = 36) // b/537525245 @ApiRequirement(minApi = EMULATOR_TESTS_MIN_API_LEVEL, onlySkipOnEmulator = true) fun testBeginRenderPass_clearsTextureCorrectly() { runBlocking {
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/EmulatorUtils.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/EmulatorUtils.kt index 42e8733..5013ff5 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/EmulatorUtils.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/EmulatorUtils.kt
@@ -37,7 +37,7 @@ }.getOrDefault(false) // Hardware check (ranchu, goldfish, cutf_cvm) - val hardwareCheck = Build.HARDWARE in listOf("ranchu", "goldfish", "cutf_cvm") + val hardwareCheck = Build.HARDWARE in listOf("ranchu", "goldfish", "cutf_cvm", "gce_x86") return qemuCheck || hardwareCheck } }
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/GPUHardwareBufferTest.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/GPUHardwareBufferTest.kt index 68f2b86..b51cd07 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/GPUHardwareBufferTest.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/GPUHardwareBufferTest.kt
@@ -48,6 +48,7 @@ @RunWith(AndroidJUnit4::class) @MediumTest @OptIn(ExperimentalWebGpuApi::class) +@androidx.test.filters.SdkSuppress(minSdkVersion = 29) class GPUHardwareBufferTest { companion object { @@ -86,7 +87,11 @@ adapter.close() instance.close() - // 2. Skip gracefully if features are missing + // 2. Skip gracefully if features are missing or running on emulator + Assume.assumeFalse( + "HardwareBuffer tests are not supported on emulator environments", + EmulatorUtils.isEmulator, + ) Assume.assumeTrue( "Adapter does not support required features for hardware buffer tests", hasRequiredFeatures @@ -266,7 +271,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testImportHardwareBufferLoop_noLeaks() { runBlocking { val unused = webGpu.execute { @@ -293,7 +298,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testWebGpuOOMScope_handlesErrorGracefully() { runBlocking { val unused = webGpu.execute { @@ -324,7 +329,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testRGBHardwareBuffer_asTexture() { runBlocking { val unused = webGpu.execute { @@ -350,7 +355,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testRGBHardwareBuffer_asExternalTexture() { runBlocking { val unused = webGpu.execute { @@ -368,7 +373,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 30) + @ApiRequirement(minApi = 30, onlySkipOnEmulator = true) fun testYUVHardwareBuffer_asExternalTexture() { runBlocking { val unused = webGpu.execute { @@ -404,7 +409,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) @SuppressLint("SuspendBlocks") fun testConcurrentAccess_raceCondition() { runBlocking(Dispatchers.IO) { @@ -430,7 +435,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testZeroCopyPipeline_writeAndVerify() { runBlocking { val unused = webGpu.execute { @@ -460,7 +465,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testZeroCopyPipeline_gpuRenderWriteAndVerify() { runBlocking { val unused = webGpu.execute { @@ -548,7 +553,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testZeroCopyPipeline_gpuSamplingAndCopyVerify() { runBlocking { val unused = webGpu.execute { @@ -616,7 +621,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 30) + @ApiRequirement(minApi = 30, onlySkipOnEmulator = true) fun testYUVExternalTexture_descriptorTransform_validatesWithoutError() { runBlocking { val unused = webGpu.execute { @@ -637,7 +642,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testRGBExternalTexture_descriptorTransform_validatesWithoutError() { runBlocking { val unused = webGpu.execute { @@ -661,7 +666,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) @SuppressLint("SuspendBlocks") fun testColorSpaceEnumVariants() { runBlocking {
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/GPUSyncFenceTest.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/GPUSyncFenceTest.kt index 788e063..2cd92d1 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/GPUSyncFenceTest.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/GPUSyncFenceTest.kt
@@ -79,7 +79,11 @@ adapter.close() instance.close() - // 2. Skip gracefully if features are missing + // 2. Skip gracefully if features are missing or running on emulator + Assume.assumeFalse( + "HardwareBuffer and SyncFence tests are not supported on emulator environments", + EmulatorUtils.isEmulator, + ) Assume.assumeTrue( "Adapter does not support required features for hardware buffer tests", hasRequiredFeatures @@ -188,7 +192,7 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 29) + @ApiRequirement(minApi = 29, onlySkipOnEmulator = true) fun testSyncFence_lifecycleAndAwaiting() = runBlocking { val unused = webGpu.execute { val (rgbBuffer, wrapper) = createTestTextureWrapper() @@ -222,7 +226,8 @@ */ @Test @MediumTest - @ApiRequirement(minApi = 33) + @ApiRequirement(minApi = 33, onlySkipOnEmulator = true) + @androidx.test.filters.SdkSuppress(minSdkVersion = 33) fun testSyncFence_fromPlatformSyncFence() = runBlocking { val unused = webGpu.execute { val (rgbBuffer, wrapper) = createTestTextureWrapper()
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/ImageTest.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/ImageTest.kt index ee5ad7f..298fade 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/ImageTest.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/ImageTest.kt
@@ -18,6 +18,7 @@ import android.graphics.BitmapFactory import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest +import androidx.test.filters.SdkSuppress import androidx.test.platform.app.InstrumentationRegistry import androidx.webgpu.WebGpuTestConstants.EMULATOR_TESTS_MIN_API_LEVEL import androidx.webgpu.helper.asString @@ -57,6 +58,7 @@ @Test @MediumTest + @SdkSuppress(maxSdkVersion = 36) // b/537525245 @ApiRequirement(minApi = EMULATOR_TESTS_MIN_API_LEVEL, onlySkipOnEmulator = true) fun imageCompareGreen() { triangleTest(GPUColor(0.2, 0.9, 0.1, 1.0), "green.png") @@ -64,6 +66,7 @@ @Test @MediumTest + @SdkSuppress(maxSdkVersion = 36) // b/537525245 @ApiRequirement(minApi = EMULATOR_TESTS_MIN_API_LEVEL, onlySkipOnEmulator = true) fun imageCompareRed() { triangleTest(GPUColor(0.9, 0.1, 0.2, 1.0), "red.png")
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/MultisampleStateTest.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/MultisampleStateTest.kt index 923732a..dd225e2 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/MultisampleStateTest.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/MultisampleStateTest.kt
@@ -17,6 +17,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest +import androidx.test.filters.SdkSuppress import androidx.webgpu.WebGpuTestConstants.EMULATOR_TESTS_MIN_API_LEVEL import androidx.webgpu.helper.createWebGpu import androidx.webgpu.helper.WebGpu @@ -184,6 +185,7 @@ } @Test + @SdkSuppress(maxSdkVersion = 36) // b/537525245 @ApiRequirement(minApi = EMULATOR_TESTS_MIN_API_LEVEL, onlySkipOnEmulator = true) fun verifyDefaultMaskEnablesAllSamplesInMSAARender() = runBlocking { val unused = webGpu.execute {
diff --git a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/QuerySetTest.kt b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/QuerySetTest.kt index a58631b..e026b78 100644 --- a/tools/android/webgpu/src/androidTest/java/androidx/webgpu/QuerySetTest.kt +++ b/tools/android/webgpu/src/androidTest/java/androidx/webgpu/QuerySetTest.kt
@@ -15,6 +15,7 @@ */ package androidx.webgpu +import androidx.test.filters.SdkSuppress import androidx.test.filters.SmallTest import androidx.webgpu.helper.WebGpu import androidx.webgpu.helper.createWebGpu @@ -458,6 +459,7 @@ * and the triangle successfully draws, the query must return the exact sample count. */ @Test + @SdkSuppress(maxSdkVersion = 36) // b/537525245 @ApiRequirement(minApi = 35, onlySkipOnEmulator = true) fun testResolveQuerySetAndReadback() { runBlocking {
diff --git a/tools/android/webgpu/src/main/java/androidx/webgpu/helper/Extensions.kt b/tools/android/webgpu/src/main/java/androidx/webgpu/helper/Extensions.kt index c63fcce..404135a 100644 --- a/tools/android/webgpu/src/main/java/androidx/webgpu/helper/Extensions.kt +++ b/tools/android/webgpu/src/main/java/androidx/webgpu/helper/Extensions.kt
@@ -18,7 +18,6 @@ import android.graphics.Color import android.hardware.SyncFence import android.os.Build -import androidx.annotation.RequiresApi import androidx.webgpu.ExperimentalWebGpuApi import androidx.webgpu.GPUColor import androidx.webgpu.GPUSyncFence @@ -40,7 +39,6 @@ * Converts an Android color long to a [GPUColor]. * @return The [GPUColor] representation of the long color. */ -@RequiresApi(Build.VERSION_CODES.O) public fun Long.toGPUColor(): GPUColor { val r = Color.red(this).toDouble() val g = Color.green(this).toDouble()
diff --git a/tools/android/webgpu/src/main/java/androidx/webgpu/helper/GPUAndroidHardwareBufferUtil.kt b/tools/android/webgpu/src/main/java/androidx/webgpu/helper/GPUAndroidHardwareBufferUtil.kt index e4cfe01..6ef9408 100644 --- a/tools/android/webgpu/src/main/java/androidx/webgpu/helper/GPUAndroidHardwareBufferUtil.kt +++ b/tools/android/webgpu/src/main/java/androidx/webgpu/helper/GPUAndroidHardwareBufferUtil.kt
@@ -17,15 +17,12 @@ package androidx.webgpu.helper import android.hardware.HardwareBuffer -import android.os.Build -import androidx.annotation.RequiresApi import androidx.webgpu.GPUDevice import androidx.webgpu.GPUExternalTextureDescriptor import androidx.webgpu.GPUHardwareBufferExternalTexture import androidx.webgpu.ExperimentalWebGpuApi import androidx.webgpu.GPUHardwareBufferTexture -@RequiresApi(Build.VERSION_CODES.O) @OptIn(ExperimentalWebGpuApi::class) public object GPUAndroidHardwareBufferUtil {
diff --git a/tools/android/webgpu/src/main/java/androidx/webgpu/helper/GPUSyncFenceHelper.kt b/tools/android/webgpu/src/main/java/androidx/webgpu/helper/GPUSyncFenceHelper.kt index 350be39..efd5c56 100644 --- a/tools/android/webgpu/src/main/java/androidx/webgpu/helper/GPUSyncFenceHelper.kt +++ b/tools/android/webgpu/src/main/java/androidx/webgpu/helper/GPUSyncFenceHelper.kt
@@ -120,13 +120,13 @@ * Creates an Android SyncFence from a raw file descriptor using EGL. * * **Ownership Transfer Rules:** - * 1. The caller transfers ownership of the raw file descriptor (`syncFd`) to this function. - * 2. If the conversion fails at any early-exit check, this function manually closes `syncFd` - * to prevent file descriptor exhaustion leaks. + * 1. The caller transfers ownership of the raw file descriptor ([pfd]) to this function. + * 2. If the conversion fails at any early-exit check, this function manually closes [pfd] to + * prevent file descriptor exhaustion leaks. * 3. Once `EGL15.eglCreateSync` is successfully called, the underlying EGL driver takes absolute * ownership of the file descriptor and guarantees its closure upon sync object destruction. * - * @param syncFd The raw file descriptor representing a synchronization fence. + * @param pfd The ParcelFileDescriptor representing a synchronization fence. * @return A valid [SyncFence] duplicated from the EGL sync object, or null if unsupported/failed. */ @OptIn(ExperimentalWebGpuApi::class)