[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, ¶ms); 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; }