[WebGPU] Migrate Android AHB JNI bindings to C++ API

Key Changes:
- Preventing memory leaks by relying on C++ RAII for automatic reference counting.
- Eliminating the need for C-style INIT macros (since C++ constructors handle `sType`).
- Removing unsafe `reinterpret_cast`s for chained struct descriptors.

Bug: b/521838697
Change-Id: Iadd191cc2f16caa481e0c90b5d45f0e903fc0ba3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/315235
Reviewed-by: Tarun Saini <sainitarun@google.com>
Reviewed-by: Loko Kung <lokokung@google.com>
Reviewed-by: Nikita Gupta <nikigupta@google.com>
Commit-Queue: Mridul Goyal <mridulgoyal@google.com>
diff --git a/tools/android/webgpu/src/main/cpp/GPUImportedAHB.cpp b/tools/android/webgpu/src/main/cpp/GPUImportedAHB.cpp
index 63d9e1f..36a5d65 100644
--- a/tools/android/webgpu/src/main/cpp/GPUImportedAHB.cpp
+++ b/tools/android/webgpu/src/main/cpp/GPUImportedAHB.cpp
@@ -5,7 +5,7 @@
 #include <poll.h>
 #include <string.h>
 #include <unistd.h>
-#include <webgpu/webgpu.h>
+#include <webgpu/webgpu_cpp.h>
 
 #include <vector>
 
@@ -26,12 +26,12 @@
 namespace dawn::kotlin_api {
 
 struct AhbTextureWrapper {
-    WGPUSharedTextureMemory stm = nullptr;
-    WGPUTexture texture = nullptr;
-    WGPUTextureView view = nullptr;
-    WGPUExternalTexture externalTexture = nullptr;
+    wgpu::SharedTextureMemory stm;
+    wgpu::Texture texture;
+    wgpu::TextureView view;
+    wgpu::ExternalTexture externalTexture;
     AHardwareBuffer* ahb = nullptr;
-    WGPUDevice device = nullptr;
+    wgpu::Device device;
 };
 
 struct UniqueFd {
@@ -65,19 +65,20 @@
 
 extern "C" {
 
-static WGPUSharedTextureMemory createSharedTextureMemoryFromAhb(JNIEnv* env,
-                                                                jobject deviceObj,
-                                                                jobject hardwareBuffer,
-                                                                AHardwareBuffer** outAhb,
-                                                                WGPUDevice* outDevice) {
+static wgpu::SharedTextureMemory createSharedTextureMemoryFromAhb(JNIEnv* env,
+                                                                  jobject deviceObj,
+                                                                  jobject hardwareBuffer,
+                                                                  AHardwareBuffer** outAhb,
+                                                                  wgpu::Device* outDevice) {
     JNIClasses* classes = JNIClasses::getInstance(env);
     jmethodID getHandle = env->GetMethodID(classes->device, "getHandle", "()J");
-    *outDevice = reinterpret_cast<WGPUDevice>(env->CallLongMethod(deviceObj, getHandle));
 
-    if (!*outDevice) {
+    WGPUDevice rawDevice = reinterpret_cast<WGPUDevice>(env->CallLongMethod(deviceObj, getHandle));
+    if (!rawDevice) {
         env->ThrowNew(classes->javaIllegalStateException, "WGPUDevice handle is null.");
         return nullptr;
     }
+    *outDevice = wgpu::Device(rawDevice);
 
     *outAhb = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer);
     if (!*outAhb) {
@@ -91,14 +92,13 @@
     // nativeDestroy.
     AHardwareBuffer_acquire(*outAhb);
 
-    WGPUSharedTextureMemoryAHardwareBufferDescriptor ahbDesc = {};
-    ahbDesc.chain.sType = WGPUSType_SharedTextureMemoryAHardwareBufferDescriptor;
+    wgpu::SharedTextureMemoryAHardwareBufferDescriptor ahbDesc = {};
     ahbDesc.handle = *outAhb;
 
-    WGPUSharedTextureMemoryDescriptor stmDesc = {};
-    stmDesc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&ahbDesc);
+    wgpu::SharedTextureMemoryDescriptor stmDesc = {};
+    stmDesc.nextInChain = &ahbDesc;
 
-    WGPUSharedTextureMemory stm = wgpuDeviceImportSharedTextureMemory(*outDevice, &stmDesc);
+    wgpu::SharedTextureMemory stm = outDevice->ImportSharedTextureMemory(&stmDesc);
     if (!stm && !env->ExceptionCheck()) {
         env->ThrowNew(classes->javaIllegalStateException,
                       "Dawn failed to import the AHardwareBuffer.");
@@ -114,10 +114,10 @@
                                                                              jint usage) {
     JNIContext c(env);
     JNIClasses* classes = JNIClasses::getInstance(env);
-    WGPUDevice device = nullptr;
+    wgpu::Device device = nullptr;
     AHardwareBuffer* ahb = nullptr;
 
-    WGPUSharedTextureMemory stm =
+    wgpu::SharedTextureMemory stm =
         createSharedTextureMemoryFromAhb(env, deviceObj, hardwareBuffer, &ahb, &device);
     if (!stm) {
         if (ahb) {
@@ -126,56 +126,52 @@
         return nullptr;
     }
 
-    if (!wgpuDeviceHasFeature(device, WGPUFeatureName_SharedTextureMemoryAHardwareBuffer)) {
-        wgpuSharedTextureMemoryRelease(stm);
+    if (!device.HasFeature(wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer)) {
         if (ahb) {
             AHardwareBuffer_release(ahb);
         }
         env->ThrowNew(classes->javaUnsupportedOperationException,
-                      "Missing WGPUFeatureName_SharedTextureMemoryAHardwareBuffer.");
+                      "Missing wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer.");
         return nullptr;
     }
 
-    WGPUSharedTextureMemoryProperties props = {};
-    wgpuSharedTextureMemoryGetProperties(stm, &props);
+    wgpu::SharedTextureMemoryProperties props = {};
+    stm.GetProperties(&props);
 
-    if (props.format == WGPUTextureFormat_Undefined) {
-        wgpuSharedTextureMemoryRelease(stm);
+    if (props.format == wgpu::TextureFormat::Undefined) {
         if (ahb) {
             AHardwareBuffer_release(ahb);
         }
-        env->ThrowNew(classes->javaUnsupportedOperationException,
-                      "AHB format maps to WGPUTextureFormat_Undefined. Dawn cannot sample this.");
+        env->ThrowNew(
+            classes->javaUnsupportedOperationException,
+            "AHB format maps to wgpu::TextureFormat::Undefined. Dawn cannot sample this.");
         return nullptr;
     }
 
-    WGPUTextureDescriptor texDesc = {};
-    texDesc.usage = static_cast<WGPUTextureUsage>(usage);
-    texDesc.dimension = WGPUTextureDimension_2D;
+    wgpu::TextureDescriptor texDesc = {};
+    texDesc.usage = static_cast<wgpu::TextureUsage>(usage);
+    texDesc.dimension = wgpu::TextureDimension::e2D;
     texDesc.size = {props.size.width, props.size.height, props.size.depthOrArrayLayers};
     texDesc.format = props.format;
     texDesc.mipLevelCount = 1;
     texDesc.sampleCount = 1;
 
-    WGPUTexture texture = wgpuSharedTextureMemoryCreateTexture(stm, &texDesc);
+    wgpu::Texture texture = stm.CreateTexture(&texDesc);
     if (!texture || env->ExceptionCheck()) {
-        if (texture) {
-            wgpuTextureRelease(texture);
-        }
-        wgpuSharedTextureMemoryRelease(stm);
         if (ahb) {
             AHardwareBuffer_release(ahb);
         }
         return nullptr;
     }
 
-    wgpuTextureAddRef(texture);
-    wgpuDeviceAddRef(device);
+    // The Java wrapper and AhbTextureWrapper independently release references on close().
+    // We explicitly add a reference here for the Java object to assume ownership of.
+    wgpuTextureAddRef(texture.Get());
     auto* wrapper = new AhbTextureWrapper{stm, texture, nullptr, nullptr, ahb, device};
 
     jobject texture_kt =
         env->NewObject(classes->texture, env->GetMethodID(classes->texture, "<init>", "(J)V"),
-                       reinterpret_cast<jlong>(texture));
+                       reinterpret_cast<jlong>(texture.Get()));
 
     jclass wrapperClz = classes->gpuHardwareBufferTexture;
     jmethodID wrapperInit =
@@ -192,10 +188,10 @@
     jobject descriptorObj) {
     JNIContext c(env);
     JNIClasses* classes = JNIClasses::getInstance(env);
-    WGPUDevice device = nullptr;
+    wgpu::Device device = nullptr;
     AHardwareBuffer* ahb = nullptr;
 
-    WGPUSharedTextureMemory stm =
+    wgpu::SharedTextureMemory stm =
         createSharedTextureMemoryFromAhb(env, deviceObj, hardwareBuffer, &ahb, &device);
     if (!stm) {
         if (ahb) {
@@ -204,61 +200,51 @@
         return nullptr;
     }
 
-    if (!wgpuDeviceHasFeature(device, WGPUFeatureName_OpaqueYCbCrAndroidForExternalTexture)) {
-        wgpuSharedTextureMemoryRelease(stm);
+    if (!device.HasFeature(wgpu::FeatureName::OpaqueYCbCrAndroidForExternalTexture)) {
         if (ahb) {
             AHardwareBuffer_release(ahb);
         }
         env->ThrowNew(classes->javaUnsupportedOperationException,
-                      "Missing WGPUFeatureName_OpaqueYCbCrAndroidForExternalTexture.");
+                      "Missing wgpu::FeatureName::OpaqueYCbCrAndroidForExternalTexture.");
         return nullptr;
     }
 
-    WGPUSharedTextureMemoryProperties props = {};
-    wgpuSharedTextureMemoryGetProperties(stm, &props);
+    wgpu::SharedTextureMemoryProperties props = {};
+    stm.GetProperties(&props);
     if (env->ExceptionCheck()) {
-        wgpuSharedTextureMemoryRelease(stm);
         if (ahb) {
             AHardwareBuffer_release(ahb);
         }
         return nullptr;
     }
 
-    if (props.format == WGPUTextureFormat_Undefined) {
-        wgpuSharedTextureMemoryRelease(stm);
+    if (props.format == wgpu::TextureFormat::Undefined) {
         if (ahb) {
             AHardwareBuffer_release(ahb);
         }
         env->ThrowNew(classes->javaUnsupportedOperationException,
-                      "YUV AHB format maps to WGPUTextureFormat_Undefined.");
+                      "YUV AHB format maps to wgpu::TextureFormat::Undefined.");
         return nullptr;
     }
 
-    WGPUTextureDescriptor texDesc = {};
-    texDesc.usage = WGPUTextureUsage_TextureBinding;
-    texDesc.dimension = WGPUTextureDimension_2D;
+    wgpu::TextureDescriptor texDesc = {};
+    texDesc.usage = wgpu::TextureUsage::TextureBinding;
+    texDesc.dimension = wgpu::TextureDimension::e2D;
     texDesc.size = {props.size.width, props.size.height, 1};
     texDesc.format = props.format;
     texDesc.mipLevelCount = 1;
     texDesc.sampleCount = 1;
 
-    WGPUTexture texture = wgpuSharedTextureMemoryCreateTexture(stm, &texDesc);
-    WGPUTextureView view = wgpuTextureCreateView(texture, nullptr);
+    wgpu::Texture texture = stm.CreateTexture(&texDesc);
+    wgpu::TextureView view = texture.CreateView();
     if (env->ExceptionCheck()) {
-        if (view) {
-            wgpuTextureViewRelease(view);
-        }
-        if (texture) {
-            wgpuTextureRelease(texture);
-        }
-        wgpuSharedTextureMemoryRelease(stm);
         if (ahb) {
             AHardwareBuffer_release(ahb);
         }
         return nullptr;
     }
 
-    WGPUExternalTextureDescriptor extDesc = {};
+    wgpu::ExternalTextureDescriptor extDesc = {};
     extDesc.plane0 = view;
 
     jclass descClass = env->GetObjectClass(descriptorObj);
@@ -291,9 +277,6 @@
     wgpu::Status status = dawn::ComputeExternalTextureParams(srcColorSpace, dstColorSpace, &params);
 
     if (status != wgpu::Status::Success) {
-        wgpuTextureViewRelease(view);
-        wgpuTextureRelease(texture);
-        wgpuSharedTextureMemoryRelease(stm);
         if (ahb) {
             AHardwareBuffer_release(ahb);
         }
@@ -328,36 +311,37 @@
     }
 
     extDesc.apparentSize = extDesc.cropSize;
-    extDesc.mirrored =
-        env->CallBooleanMethod(descriptorObj, env->GetMethodID(descClass, "isMirrored", "()Z"));
+    extDesc.mirrored = static_cast<bool>(
+        env->CallBooleanMethod(descriptorObj, env->GetMethodID(descClass, "isMirrored", "()Z")));
 
     jint rotation =
         env->CallIntMethod(descriptorObj, env->GetMethodID(descClass, "getRotation", "()I"));
     switch (rotation) {
         case 2:
-            extDesc.rotation = WGPUExternalTextureRotation_Rotate90Degrees;
+            extDesc.rotation = wgpu::ExternalTextureRotation::Rotate90Degrees;
             break;
         case 3:
-            extDesc.rotation = WGPUExternalTextureRotation_Rotate180Degrees;
+            extDesc.rotation = wgpu::ExternalTextureRotation::Rotate180Degrees;
             break;
         case 4:
-            extDesc.rotation = WGPUExternalTextureRotation_Rotate270Degrees;
+            extDesc.rotation = wgpu::ExternalTextureRotation::Rotate270Degrees;
             break;
         default:
-            extDesc.rotation = WGPUExternalTextureRotation_Rotate0Degrees;
+            extDesc.rotation = wgpu::ExternalTextureRotation::Rotate0Degrees;
             break;
     }
 
-    WGPUExternalTexture externalTexture = wgpuDeviceCreateExternalTexture(device, &extDesc);
+    wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&extDesc);
     env->DeleteLocalRef(descClass);
 
-    wgpuExternalTextureAddRef(externalTexture);
-    wgpuDeviceAddRef(device);
+    // The Java wrapper and AhbTextureWrapper independently release references on close().
+    // We explicitly add a reference here for the Java object to assume ownership of.
+    wgpuExternalTextureAddRef(externalTexture.Get());
     auto* wrapper = new AhbTextureWrapper{stm, texture, view, externalTexture, ahb, device};
 
     jobject extTex_kt = env->NewObject(classes->externalTexture,
                                        env->GetMethodID(classes->externalTexture, "<init>", "(J)V"),
-                                       reinterpret_cast<jlong>(externalTexture));
+                                       reinterpret_cast<jlong>(externalTexture.Get()));
 
     jclass wrapperClz = classes->gpuHardwareBufferExternalTexture;
     jmethodID wrapperInit =
@@ -374,7 +358,7 @@
     auto* wrapper = reinterpret_cast<AhbTextureWrapper*>(handle);
     JNIClasses* classes = JNIClasses::getInstance(env);
 
-    std::vector<WGPUSharedFence> importedFences;
+    std::vector<wgpu::SharedFence> importedFences;
     if (fences != nullptr) {
         jsize len = env->GetArrayLength(fences);
         if (len > 0) {
@@ -386,15 +370,13 @@
                     // Dawn's ImportSharedFence internally duplicates (dup) the file descriptor.
                     // This allows Java's ParcelFileDescriptor to retain ownership of the original
                     // FD and close it safely when it gets garbage collected, preventing leaks.
-                    WGPUSharedFenceSyncFDDescriptor syncFdDesc = {};
-                    syncFdDesc.chain.sType = WGPUSType_SharedFenceSyncFDDescriptor;
+                    wgpu::SharedFenceSyncFDDescriptor syncFdDesc = {};
                     syncFdDesc.handle = fd;
 
-                    WGPUSharedFenceDescriptor fenceDesc = {};
-                    fenceDesc.nextInChain = &syncFdDesc.chain;
+                    wgpu::SharedFenceDescriptor fenceDesc = {};
+                    fenceDesc.nextInChain = &syncFdDesc;
 
-                    WGPUSharedFence fence =
-                        wgpuDeviceImportSharedFence(wrapper->device, &fenceDesc);
+                    wgpu::SharedFence fence = wrapper->device.ImportSharedFence(&fenceDesc);
                     if (fence) {
                         importedFences.push_back(fence);
                     }
@@ -404,28 +386,22 @@
         }
     }
 
-    WGPUSharedTextureMemoryBeginAccessDescriptor beginDesc = {};
+    wgpu::SharedTextureMemoryBeginAccessDescriptor beginDesc = {};
     beginDesc.fenceCount = importedFences.size();
     beginDesc.fences = importedFences.data();
     beginDesc.concurrentRead = false;
     beginDesc.initialized = true;
 
-    WGPUSharedTextureMemoryVkImageLayoutBeginState beginLayout = {};
-
-    beginLayout.chain.sType = WGPUSType_SharedTextureMemoryVkImageLayoutBeginState;
-    beginLayout.chain.next = nullptr;
-    // Assuming the AHB comes in as GENERAL from the Android producer
-    beginLayout.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
-    // Transition for Dawn to sample the texture
-    beginLayout.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
-
-    beginDesc.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&beginLayout.chain);
-
-    wgpuSharedTextureMemoryBeginAccess(wrapper->stm, wrapper->texture, &beginDesc);
-
-    for (WGPUSharedFence f : importedFences) {
-        wgpuSharedFenceRelease(f);
+    wgpu::SharedTextureMemoryVkImageLayoutBeginState beginLayout = {};
+    wgpu::AdapterInfo adapterInfo;
+    wrapper->device.GetAdapterInfo(&adapterInfo);
+    if (adapterInfo.backendType == wgpu::BackendType::Vulkan) {
+        beginLayout.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
+        beginLayout.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+        beginDesc.nextInChain = &beginLayout;
     }
+
+    wrapper->stm.BeginAccess(wrapper->texture, &beginDesc);
 }
 
 JNIEXPORT jint JNICALL Java_androidx_webgpu_GPUHardwareBufferWrapper_nativeEndAccess(JNIEnv* env,
@@ -434,18 +410,18 @@
     auto* wrapper = reinterpret_cast<AhbTextureWrapper*>(handle);
     JNIClasses* classes = JNIClasses::getInstance(env);
 
-    WGPUSharedTextureMemoryEndAccessState endState = {};
-    WGPUSharedTextureMemoryVkImageLayoutEndState endLayout = {};
+    wgpu::SharedTextureMemoryEndAccessState endState = {};
+    wgpu::SharedTextureMemoryVkImageLayoutEndState endLayout = {};
+    wgpu::AdapterInfo adapterInfo;
+    wrapper->device.GetAdapterInfo(&adapterInfo);
+    if (adapterInfo.backendType == wgpu::BackendType::Vulkan) {
+        endLayout.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+        endLayout.newLayout = VK_IMAGE_LAYOUT_GENERAL;
+        endState.nextInChain = &endLayout;
+    }
 
-    endLayout.chain.sType = WGPUSType_SharedTextureMemoryVkImageLayoutEndState;
-    endLayout.chain.next = nullptr;
-    // Use layouts that preserve data
-    endLayout.oldLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
-    endLayout.newLayout = VK_IMAGE_LAYOUT_GENERAL;
-    endState.nextInChain = reinterpret_cast<WGPUChainedStruct*>(&endLayout.chain);
-
-    WGPUStatus status = wgpuSharedTextureMemoryEndAccess(wrapper->stm, wrapper->texture, &endState);
-    if (status != WGPUStatus_Success) {
+    wgpu::Status status = wrapper->stm.EndAccess(wrapper->texture, &endState);
+    if (status != wgpu::Status::Success) {
         env->ThrowNew(classes->javaIllegalStateException,
                       "Failed to end access on SharedTextureMemory.");
         return -1;
@@ -455,15 +431,13 @@
     bool failed = false;
     if (endState.fenceCount > 0) {
         for (size_t i = 0; i < endState.fenceCount && !failed; ++i) {
-            WGPUSharedFence cFence = endState.fences[i];
+            wgpu::SharedFence cFence = endState.fences[i];
 
-            WGPUSharedFenceExportInfo exportInfo = {};
-            WGPUSharedFenceSyncFDExportInfo syncFdExportInfo = {};
-            syncFdExportInfo.chain.sType = WGPUSType_SharedFenceSyncFDExportInfo;
-            syncFdExportInfo.chain.next = nullptr;
-            exportInfo.nextInChain = &syncFdExportInfo.chain;
+            wgpu::SharedFenceExportInfo exportInfo = {};
+            wgpu::SharedFenceSyncFDExportInfo syncFdExportInfo = {};
+            exportInfo.nextInChain = &syncFdExportInfo;
 
-            wgpuSharedFenceExportInfo(cFence, &exportInfo);
+            cFence.ExportInfo(&exportInfo);
             int fd = syncFdExportInfo.handle;
             if (fd >= 0) {
                 // Dawn's ExportInfo borrows the FD; Dawn retains ownership of the underlying fence
@@ -479,8 +453,6 @@
         }
     }
 
-    wgpuSharedTextureMemoryEndAccessStateFreeMembers(endState);
-
     UniqueFd mergedFd(-1);
     for (size_t i = 0; i < fds.size() && !failed; ++i) {
         UniqueFd& fd = fds[i];
@@ -512,24 +484,9 @@
                                                                                    jlong handle) {
     auto* wrapper = reinterpret_cast<AhbTextureWrapper*>(handle);
 
-    if (wrapper->externalTexture) {
-        wgpuExternalTextureRelease(wrapper->externalTexture);
-    }
-    if (wrapper->view) {
-        wgpuTextureViewRelease(wrapper->view);
-    }
-    if (wrapper->texture) {
-        wgpuTextureRelease(wrapper->texture);
-    }
-    if (wrapper->stm) {
-        wgpuSharedTextureMemoryRelease(wrapper->stm);
-    }
     if (wrapper->ahb) {
         AHardwareBuffer_release(wrapper->ahb);
     }
-    if (wrapper->device) {
-        wgpuDeviceRelease(wrapper->device);
-    }
 
     delete wrapper;
 }