Skip to content

QVAC-19798 feat[api]: add OpenCL GGML backend support for ocr-ggml EasyOCR and DocTR#2490

Open
Alok-Ranjan23 wants to merge 5 commits into
mainfrom
feat/QVAC-19798-add-opencl-ggml-backend-ocr-ggml
Open

QVAC-19798 feat[api]: add OpenCL GGML backend support for ocr-ggml EasyOCR and DocTR#2490
Alok-Ranjan23 wants to merge 5 commits into
mainfrom
feat/QVAC-19798-add-opencl-ggml-backend-ocr-ggml

Conversation

@Alok-Ranjan23

Copy link
Copy Markdown
Contributor

Summary

Adds opt-in OpenCL GGML backend support to @qvac/ocr-ggml for both the EasyOCR and DocTR pipelines, with transparent CPU fallback and backend-identity reporting — completing the GPU-backend matrix alongside the existing Vulkan/Metal support.

The Vulkan/Metal work (QVAC-19797) already refactored all four pipeline steps (EasyOCR CRAFT + CRNN, DocTR DBNet + recognizer) to run on a generic injected ggml_backend_dev_t resolved by OcrBackendSelection. So this change needs no step-level rewrites — it extends the selection layer, the JS/TS API, the build, and tests.

What changed

  • OcrTypes.hpp — add BackendDevice::OPENCL.
  • OcrBackendSelection.{hpp,cpp} — add isOpenCLBackendName() (matches "opencl", case-insensitive, e.g. ggml's GPUOpenCL device / OpenCL reg name) and an OPENCL case in selectBackendDevice(). It calls trySelectGpu(..., rejectAdreno=false): unlike Vulkan, Adreno is NOT skipped for OpenCL — OpenCL is Adreno's sound compute path (the inverse of the Vulkan Adreno guard). CPU fallback + fallbackReason behaviour is unchanged.
  • AddonJs.hpp / index.d.ts / index.js — accept backendDevice: 'opencl' (the value is already forwarded generically; only validation/types/docs change).
  • vcpkg.json — add the opencl port (platform: android) so the OpenCL loader/headers are present, mirroring vla-ggml / llm-llamacpp / embed-llamacpp. The existing CMakeLists.txt BACKEND_DL_LIBS loop already exports any built ggml-opencl backend into prebuilds/.
  • Tests
    • test/integration/opencl-backend.test.js: desktop opt-in contract — requesting 'opencl' either selects an OpenCL GPU or reports an explicit CPU fallback; gated on a shipped libggml-opencl lib so it skips cleanly where the backend wasn't built.
    • test/integration/android-opencl.test.js: Android/Adreno path, accuracy-gated (OCR output must be correct on whichever backend resolves).
    • findOpenCLBackendLib() helper in utils.js; both wrappers registered in test/mobile/test-groups.json + regenerated integration.auto.cjs.
  • README.md — document the 'opencl' option, the Adreno rationale, and the Android-focused build/driver caveats.

Notes / scope

  • CPU remains the default; all existing EasyOCR/DocTR behaviour is unchanged. 'opencl' is purely opt-in.
  • OpenCL here is Android/Adreno-focused — the opencl vcpkg port is Android-only across the monorepo, and ggml-opencl isn't built for desktop Linux/Windows or Apple. So the desktop test exercises the fallback contract, while real OpenCL execution is validated on the Android device farm (Adreno: S25 Ultra / S26), which the existing on-pr-ocr-ggml workflow runs.

Test plan

  • JS lint (standard) clean on touched files
  • clang-format (LLVM 22) clean on changed C++ lines
  • CI: cpp-lint (clang-format + clang-tidy)
  • CI: native prebuilds (incl. android-arm64 with the OpenCL backend)
  • CI: desktop integration tests (CPU default unaffected; OpenCL test skips without the lib)
  • CI: Android device-farm — android-opencl exercises the Adreno OpenCL path with the accuracy gate

Note: local native build/test couldn't run in the dev WSL toolchain (missing linux-x64 bare/bare-make runtime binaries); CI is the build/test gate.

Asana: https://app.asana.com/1/45238840754660/project/1214153063536860/task/1215288817867554

Extend the existing CPU/Vulkan/Metal backend selection with an OpenCL option
for both the EasyOCR and DocTR pipelines. The Vulkan/Metal framework already
injects a generic ggml_backend_dev_t into all four pipeline steps, so no
step-level changes are needed — this wires the selection + API + build + tests.

- OcrTypes.hpp: add BackendDevice::OPENCL.
- OcrBackendSelection.{hpp,cpp}: add isOpenCLBackendName() + an OPENCL case in
  selectBackendDevice (trySelectGpu with rejectAdreno=false — OpenCL is Adreno's
  sound GPU path, the inverse of the guarded Vulkan path). CPU fallback +
  fallbackReason preserved.
- AddonJs.hpp / index.d.ts / index.js: accept backendDevice: 'opencl'.
- vcpkg.json: add the opencl port (platform: android) so the OpenCL loader is
  present, mirroring vla-ggml / llm-llamacpp / embed-llamacpp.
- tests: opencl-backend.test.js (desktop opt-in: OpenCL selected OR explicit
  CPU fallback) and android-opencl.test.js (Adreno path, accuracy-gated);
  findOpenCLBackendLib() helper; mobile grouping + regenerated runner.
- README: document the 'opencl' option and Android/Adreno caveats.

CPU stays the default and all existing behaviour is unchanged. OpenCL is
Android/Adreno-focused, so real GPU execution is validated on Android CI.
@Alok-Ranjan23 Alok-Ranjan23 requested review from a team as code owners June 8, 2026 17:35
Comment thread packages/ocr-ggml/test/mobile/integration.auto.cjs Dismissed
Comment thread packages/ocr-ggml/test/mobile/integration.auto.cjs Dismissed
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Tier-based Approval Status

**PR Tier:** TIER1

**Current Status:** ❌ PENDING

**Requirements:**
- 1 Team Member approval ❌ (0/1)
- 1 Team Lead OR Management approval ❌ (0/1)



---
*This comment is automatically updated when reviews change.*

@Alok-Ranjan23 Alok-Ranjan23 added verify tier1 verified Authorize secrets / label-gate in PR workflows labels Jun 8, 2026
@Alok-Ranjan23 Alok-Ranjan23 self-assigned this Jun 8, 2026
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Mobile integration tests — @qvac/ocr-ggml (iOS)

Result: passed

metric value
Devices passed 6
Devices failed 0
Test cases total 18
Test cases passed 18
Test cases failed 0
Test cases skipped 0

View workflow run

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Mobile integration tests — @qvac/ocr-ggml (Android)

Result: passed

metric value
Devices passed 9
Devices failed 0
Test cases total 27
Test cases passed 27
Test cases failed 0
Test cases skipped 0

View workflow run

CI on PR #2490 showed that requesting backendDevice:'opencl' on Adreno
correctly selects the OpenCL device (GPUOpenCL) but then GGML_ABORTs in the
CRAFT detector: ggml's OpenCL backend does not implement POOL_2D
(ggml-opencl.cpp: "op not supported node_22 (POOL_2D)" -> SIGABRT). ggml aborts
on any unsupported op rather than failing gracefully, crashing the process.

Add a POOL_2D op-support probe (ggml_backend_dev_supports_op) in
OcrBackendSelection::trySelectGpu: a matched GPU device that cannot run the op
family every OCR graph needs is rejected in favour of CPU with a clear
fallbackReason, instead of crashing at inference. POOL_2D is used by all four
OCR graphs and is exactly the op ggml's OpenCL backend is missing; the probe
mirrors easyocr/craft.cpp maxpool_2x2 so full-featured Vulkan/Metal devices
(which run OCR for real) always pass and are unaffected.

Net: requesting 'opencl' is now safe (never crashes) and transparently resolves
to CPU for OCR until ggml's OpenCL backend gains the required vision ops
(POOL_2D / conv / upscale / transpose-conv) — tracked as a follow-up. README
updated to document the op-support gate and CPU fallback.
…backend-ocr-ggml' into feat/QVAC-19798-add-opencl-ggml-backend-ocr-ggml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tier1 verified Authorize secrets / label-gate in PR workflows verify

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant