@@ -146,68 +146,55 @@ set(LLAMACPP_BACKEND_SOURCES
146146 llamacpp_backend.cpp
147147 rac_llm_llamacpp.cpp
148148 rac_backend_llamacpp_register.cpp
149- # GAP 02 Phase 8: unified engine plugin entry point. Coexists with the
150- # legacy rac_backend_llamacpp_register() bootstrap; both wrap the same
151- # ops-struct.
149+ # Unified ABI plugin entry — fills both llm_ops and vlm_ops slots.
152150 rac_plugin_entry_llamacpp.cpp
151+ # VLM Backend (vision-language as a modality of the same engine).
152+ rac_vlm_llamacpp.cpp
153+ # VLM ops vtable (the g_llamacpp_vlm_ops struct consumed by the unified
154+ # plugin entry). Extracted from the (now-deleted) VLM register file when
155+ # the two plugin entries were unified.
156+ rac_llamacpp_vlm_ops.cpp
153157)
154158
155159set (LLAMACPP_BACKEND_HEADERS
156160 llamacpp_backend.h
157161)
158162
159- # Option to enable VLM multimodal support (requires mtmd from llama.cpp)
160- option (RAC_VLM_USE_MTMD "Enable VLM multimodal support via llama.cpp mtmd" ON )
161- if (RAC_VLM_USE_MTMD)
162- message (STATUS "VLM multimodal support enabled" )
163- # Add VLM Backend sources (Vision Language Model)
164- list (APPEND LLAMACPP_BACKEND_SOURCES
165- rac_vlm_llamacpp.cpp
166- rac_backend_llamacpp_vlm_register.cpp
167- # GAP 02 Phase 8: unified engine plugin entry point for VLM.
168- rac_plugin_entry_llamacpp_vlm.cpp
169- )
170- # Add mtmd sources from llama.cpp tools directory. mtmd-image.cpp
171- # carries the `mtmd_image_preprocessor_*` vtables that mtmd.cpp
172- # references during `mtmd_context::init_vision()`; without it ld.lld
173- # on Linux/Android complains:
174- # ld.lld: error: undefined symbol: vtable for
175- # mtmd_image_preprocessor_{llava_uhd,step3vl,idefics3,internvl}
176- list (APPEND LLAMACPP_BACKEND_SOURCES
177- ${llamacpp_SOURCE_DIR} /tools/mtmd/mtmd.cpp
178- ${llamacpp_SOURCE_DIR} /tools/mtmd/mtmd-helper.cpp
179- ${llamacpp_SOURCE_DIR} /tools/mtmd/mtmd-audio.cpp
180- ${llamacpp_SOURCE_DIR} /tools/mtmd/mtmd-image.cpp
181- ${llamacpp_SOURCE_DIR} /tools/mtmd/clip.cpp
182- )
183- # Glob every mtmd model implementation upstream ships. The previous
184- # hand-curated list drifts each llama.cpp bump (b9180 adds
185- # deepseekocr / dotsocr / gemma4a / gemma4v / granite-speech /
186- # hunyuanocr / mimovl / qwen3a / step3vl / yasa2 over what we
187- # listed). On Linux/Android the resulting clip_image_build_graph
188- # references unconditionally need every clip_graph_<name>::* vtable
189- # to be present at link time, otherwise ld.lld bails with
190- # ld.lld: error: undefined symbol: vtable for clip_graph_<name>
191- # `CONFIGURE_DEPENDS` re-globs when the directory changes.
192- #
193- # pass2-syn-063: CONFIGURE_DEPENDS is best-effort on Visual Studio /
194- # Xcode generators (CMake docs: "a generator may not implement this
195- # feature"). All RAC CMakePresets that build this engine use Ninja
196- # (macos-debug, macos-release, linux-asan, android-arm64, ios-*),
197- # so re-glob fires reliably for our CI matrix. Developers who run an
198- # Xcode-driven CMake build of the C++ engine for direct on-device
199- # profiling MUST `cmake --reconfigure` after an upstream llama.cpp
200- # bump that adds a new tools/mtmd/models/*.cpp file; otherwise the
201- # build fails at link time with an undefined vtable for
202- # clip_graph_<new_model>. The STATUS message below prints the
203- # discovered count to make a silent drop loud during local builds.
204- file (GLOB _RAC_MTMD_MODEL_SOURCES CONFIGURE_DEPENDS
205- "${llamacpp_SOURCE_DIR} /tools/mtmd/models/*.cpp"
206- )
207- list (LENGTH _RAC_MTMD_MODEL_SOURCES _RAC_MTMD_MODEL_COUNT)
208- message (STATUS "VLM mtmd model implementations discovered: ${_RAC_MTMD_MODEL_COUNT} (llama.cpp ${LLAMACPP_VERSION} )" )
209- list (APPEND LLAMACPP_BACKEND_SOURCES ${_RAC_MTMD_MODEL_SOURCES} )
210- endif ()
163+ # VLM multimodal support is always compiled in — llama.cpp is one engine
164+ # that supports both LLM and VLM modalities. The two are exposed through
165+ # a single unified plugin vtable, not two separate plugins.
166+ #
167+ # Add mtmd sources from llama.cpp tools directory. mtmd-image.cpp carries
168+ # the `mtmd_image_preprocessor_*` vtables that mtmd.cpp references during
169+ # `mtmd_context::init_vision()`; without it ld.lld on Linux/Android complains:
170+ # ld.lld: error: undefined symbol: vtable for
171+ # mtmd_image_preprocessor_{llava_uhd,step3vl,idefics3,internvl}
172+ list (APPEND LLAMACPP_BACKEND_SOURCES
173+ ${llamacpp_SOURCE_DIR} /tools/mtmd/mtmd.cpp
174+ ${llamacpp_SOURCE_DIR} /tools/mtmd/mtmd-helper.cpp
175+ ${llamacpp_SOURCE_DIR} /tools/mtmd/mtmd-audio.cpp
176+ ${llamacpp_SOURCE_DIR} /tools/mtmd/mtmd-image.cpp
177+ ${llamacpp_SOURCE_DIR} /tools/mtmd/clip.cpp
178+ )
179+
180+ # Glob every mtmd model implementation upstream ships. The hand-curated list
181+ # drifts each llama.cpp bump (b9180 adds deepseekocr / dotsocr / gemma4a /
182+ # gemma4v / granite-speech / hunyuanocr / mimovl / qwen3a / step3vl / yasa2
183+ # etc.). On Linux/Android the resulting clip_image_build_graph references
184+ # unconditionally need every clip_graph_<name>::* vtable to be present at
185+ # link time, otherwise ld.lld bails with
186+ # ld.lld: error: undefined symbol: vtable for clip_graph_<name>
187+ # `CONFIGURE_DEPENDS` re-globs when the directory changes; it is best-effort
188+ # on Visual Studio / Xcode generators but reliable on Ninja (which RAC's
189+ # CMakePresets use). Developers running an Xcode-driven CMake build for
190+ # on-device profiling MUST `cmake --reconfigure` after an upstream llama.cpp
191+ # bump that adds a new tools/mtmd/models/*.cpp file.
192+ file (GLOB _RAC_MTMD_MODEL_SOURCES CONFIGURE_DEPENDS
193+ "${llamacpp_SOURCE_DIR} /tools/mtmd/models/*.cpp"
194+ )
195+ list (LENGTH _RAC_MTMD_MODEL_SOURCES _RAC_MTMD_MODEL_COUNT)
196+ message (STATUS "VLM mtmd model implementations discovered: ${_RAC_MTMD_MODEL_COUNT} (llama.cpp ${LLAMACPP_VERSION} )" )
197+ list (APPEND LLAMACPP_BACKEND_SOURCES ${_RAC_MTMD_MODEL_SOURCES} )
211198
212199# GAP 06 close-out (Phase 1 / B1): migrated to rac_add_engine_plugin().
213200#
@@ -257,41 +244,32 @@ rac_add_engine_plugin(llamacpp
257244 ${llamacpp_SOURCE_DIR} /ggml/include
258245 ${llamacpp_SOURCE_DIR} /vendor # nlohmann/json.hpp
259246 COMPILE_DEFINITIONS RAC_LLAMACPP_BUILDING
260- PRIMITIVES GENERATE_TEXT
247+ PRIMITIVES GENERATE_TEXT VLM
261248 RUNTIMES CPU METAL CUDA
262249 FORMATS GGUF GGML BIN
263250 AVAILABILITY PUBLIC
264251 PACKAGE_OWNER runanywhere
265252 PACKAGE_NAME runanywhere_llamacpp
266253)
267254
268- # VLM multimodal mtmd includes/defs — engine-specific, applied after the macro.
269- if (RAC_VLM_USE_MTMD)
270- target_include_directories (rac_backend_llamacpp PRIVATE
271- ${llamacpp_SOURCE_DIR} /tools/mtmd
272- ${llamacpp_SOURCE_DIR} /tools/mtmd/models
273- )
274- target_compile_definitions (rac_backend_llamacpp PRIVATE RAC_VLM_USE_MTMD=1 )
275- endif ()
255+ # VLM multimodal mtmd includes — engine-specific, applied after the macro.
256+ # mtmd is always compiled in (one engine, two modalities).
257+ target_include_directories (rac_backend_llamacpp PRIVATE
258+ ${llamacpp_SOURCE_DIR} /tools/mtmd
259+ ${llamacpp_SOURCE_DIR} /tools/mtmd/models
260+ )
276261
277262# Self-registration carrier: thin shim that wraps RAC_STATIC_PLUGIN_REGISTER.
263+ # Since llama.cpp is one engine with both LLM and VLM modalities exposed by
264+ # the same plugin vtable, there is a single static-register shim and a single
265+ # SHARED carrier (no separate _vlm variants).
278266if (RAC_STATIC_PLUGINS)
279267 # Static path: ctor lands inside rac_commons so it runs before main().
280268 # rac_commons PUBLIC-links rac_backend_llamacpp below so the entry
281269 # symbol resolves at link time.
282270 target_sources (rac_commons PRIVATE
283271 ${CMAKE_CURRENT_SOURCE_DIR} /rac_static_register_llamacpp.cpp
284272 )
285- if (RAC_VLM_USE_MTMD)
286- # Swift-only E2E Phase 6e: VLM plugin needs its own static-register
287- # ctor to be routable via rac_plugin_route(framework=llamacpp,
288- # primitive=vlm). Without this, only the LLM plugin registers at
289- # process start and VLM loads fail with
290- # "no backend route for llamacpp_vlm".
291- target_sources (rac_commons PRIVATE
292- ${CMAKE_CURRENT_SOURCE_DIR} /rac_static_register_llamacpp_vlm.cpp
293- )
294- endif ()
295273 target_include_directories (rac_commons PRIVATE
296274 ${CMAKE_CURRENT_SOURCE_DIR}
297275 ${RAC_COMMONS_ROOT_DIR} /include
@@ -313,33 +291,6 @@ else()
313291 )
314292 rac_apply_android_page_alignment (runanywhere_llamacpp )
315293 install (TARGETS runanywhere_llamacpp LIBRARY DESTINATION lib)
316-
317- if (RAC_VLM_USE_MTMD)
318- # Parallel SHARED carrier for the VLM vtable so dynamic-plugin hosts
319- # can route RAC_PRIMITIVE_VLM (name="llamacpp_vlm") via
320- # `rac_registry_load_plugin(librunanywhere_llamacpp_vlm.*)`. The
321- # dlopen loader derives the entry-symbol name from the library
322- # filename (see plugin_loader.cpp::entry_symbol_from_path), so the
323- # carrier's basename must match `runanywhere_llamacpp_vlm` to map to
324- # `rac_plugin_entry_llamacpp_vlm`. Without this, a host that does NOT
325- # call `rac_backend_llamacpp_vlm_register()` directly (the Swift /
326- # Kotlin / RN bridge path) cannot reach the VLM plugin even though
327- # the implementation is linked into rac_backend_llamacpp.
328- add_library (runanywhere_llamacpp_vlm SHARED
329- ${CMAKE_CURRENT_SOURCE_DIR} /rac_static_register_llamacpp_vlm.cpp
330- )
331- set_target_properties (runanywhere_llamacpp_vlm PROPERTIES
332- OUTPUT_NAME runanywhere_llamacpp_vlm
333- C_VISIBILITY_PRESET hidden
334- CXX_VISIBILITY_PRESET hidden
335- )
336- target_link_libraries (runanywhere_llamacpp_vlm PUBLIC
337- rac_backend_llamacpp
338- rac_commons
339- )
340- rac_apply_android_page_alignment (runanywhere_llamacpp_vlm )
341- install (TARGETS runanywhere_llamacpp_vlm LIBRARY DESTINATION lib)
342- endif ()
343294endif ()
344295
345296# =============================================================================
0 commit comments