#include "neural_runtime.hpp" #include #include #include #include #include #include #include #include #include #include #include #if defined(MOTIONBRICKS_HAVE_GGML) #include #include #if defined(MOTIONBRICKS_HAVE_VULKAN) #include #endif #endif namespace motionbricks::detail { #if defined(MOTIONBRICKS_HAVE_GGML) namespace { struct ggml_context_deleter { void operator()(ggml_context * value) const noexcept { ggml_free(value); } }; struct gguf_context_deleter { void operator()(gguf_context * value) const noexcept { gguf_free(value); } }; struct backend_deleter { void operator()(ggml_backend * value) const noexcept { ggml_backend_free(value); } }; struct buffer_deleter { void operator()(ggml_backend_buffer * value) const noexcept { ggml_backend_buffer_free(value); } }; using context_ptr = std::unique_ptr; using gguf_ptr = std::unique_ptr; using backend_ptr = std::unique_ptr; using buffer_ptr = std::unique_ptr; bool copy_gguf_data(const std::filesystem::path & path, ggml_context * tensors, const gguf_context * metadata, std::string & reason) { std::unique_ptr file( std::fopen(path.string().c_str(), "rb"), &std::fclose); if (!file) { reason = "cannot open GGUF weights: " + path.string(); return false; } std::vector staging(4U * 1024U * 1024U); const auto count = gguf_get_n_tensors(metadata); for (std::int64_t index = 0; index < count; ++index) { const char * name = gguf_get_tensor_name(metadata, index); auto * tensor = ggml_get_tensor(tensors, name); if (tensor == nullptr) { reason = std::string("missing allocated tensor: ") + name; return false; } const auto offset = gguf_get_data_offset(metadata) + gguf_get_tensor_offset(metadata, index); if (offset > static_cast(std::numeric_limits::max()) || std::fseek(file.get(), static_cast(offset), SEEK_SET) != 0) { reason = std::string("cannot seek to tensor data: ") + name; return false; } const auto bytes = ggml_nbytes(tensor); for (std::size_t position = 0; position < bytes; position += staging.size()) { const auto amount = std::min(staging.size(), bytes - position); if (std::fread(staging.data(), 1, amount, file.get()) != amount) { reason = std::string("truncated tensor data: ") + name; return false; } ggml_backend_tensor_set(tensor, staging.data(), position, amount); } } return true; } } // namespace class neural_runtime { public: struct component { std::string name; context_ptr context; buffer_ptr buffer; }; backend_ptr backend; std::vector components; }; namespace { mb_status open_component(neural_runtime & runtime, const std::filesystem::path & path, std::string name, std::string & reason) { ggml_context * raw_context = nullptr; const gguf_init_params params{true, &raw_context}; gguf_ptr metadata(gguf_init_from_file(path.string().c_str(), params)); context_ptr context(raw_context); if (!metadata || !context) { reason = "cannot initialize GGUF weights: " + path.string(); return MB_INVALID_FORMAT; } buffer_ptr buffer(ggml_backend_alloc_ctx_tensors(context.get(), runtime.backend.get())); if (!buffer) { reason = "cannot allocate backend weights for " + path.string(); return MB_OUT_OF_MEMORY; } if (!copy_gguf_data(path, context.get(), metadata.get(), reason)) return MB_IO_ERROR; runtime.components.push_back({std::move(name), std::move(context), std::move(buffer)}); return MB_OK; } } // namespace mb_status create_neural_runtime(const std::filesystem::path & bundle, mb_device device, std::uint32_t threads, const std::string & backend_directory, std::shared_ptr & output, std::string & reason) { output.reset(); if (!backend_directory.empty()) ggml_backend_load_all_from_path(backend_directory.c_str()); auto runtime = std::make_shared(); const auto selected = device == MB_DEVICE_AUTO ? MB_DEVICE_CPU : device; if (selected == MB_DEVICE_CPU) { runtime->backend.reset(ggml_backend_cpu_init()); if (runtime->backend) { const auto hardware = std::max(1U, std::thread::hardware_concurrency()); const auto count = threads == 0U ? hardware : threads; ggml_backend_cpu_set_n_threads(runtime->backend.get(), static_cast(count)); } } else if (selected == MB_DEVICE_VULKAN) { #if defined(MOTIONBRICKS_HAVE_VULKAN) // GGML's Vulkan F16/cooperative-matrix fast paths may be selected even // for F32 weights. The released MotionBricks baseline is explicitly // F32, and those paths create material decoder drift after many // residual convolutions. Keep the parity runtime strictly F32. #if defined(_WIN32) _putenv_s("GGML_VK_DISABLE_F16", "1"); _putenv_s("GGML_VK_DISABLE_COOPMAT", "1"); _putenv_s("GGML_VK_DISABLE_COOPMAT2", "1"); #else setenv("GGML_VK_DISABLE_F16", "1", 0); setenv("GGML_VK_DISABLE_COOPMAT", "1", 0); setenv("GGML_VK_DISABLE_COOPMAT2", "1", 0); #endif runtime->backend.reset(ggml_backend_vk_init(0)); #else reason = "this build has no Vulkan backend"; return MB_BACKEND_UNAVAILABLE; #endif } if (!runtime->backend) { reason = selected == MB_DEVICE_VULKAN ? "cannot initialize Vulkan backend" : "cannot initialize CPU backend"; return MB_BACKEND_UNAVAILABLE; } constexpr std::array files{ std::pair{"pose", "pose.gguf"}, std::pair{"root", "root.gguf"}, std::pair{"vq-decoder", "vq-decoder.gguf"}, std::pair{"support", "support.gguf"}, }; for (const auto & [name, filename] : files) { const auto status = open_component(*runtime, bundle / filename, name, reason); if (status != MB_OK) return status; } output = std::move(runtime); return MB_OK; } ggml_backend_t neural_backend(const neural_runtime & runtime) noexcept { return runtime.backend.get(); } ggml_tensor * neural_weight(const neural_runtime & runtime, std::string_view component, std::string_view name) noexcept { const auto found = std::find_if(runtime.components.begin(), runtime.components.end(), [&](const neural_runtime::component & item) { return item.name == component; }); if (found == runtime.components.end()) return nullptr; std::string owned(name); if (auto * direct = ggml_get_tensor(found->context.get(), owned.c_str())) return direct; if (owned.size() >= 64U) { const std::string needle = "self_attn."; if (const auto position = owned.find(needle); position != std::string::npos) owned.replace(position, needle.size(), "attn."); } return ggml_get_tensor(found->context.get(), owned.c_str()); } bool neural_copy_f32(const neural_runtime & runtime, std::string_view component, std::string_view name, std::vector & output, std::string & reason) { auto * tensor = neural_weight(runtime, component, name); if (tensor == nullptr) { reason = "missing neural tensor: " + std::string(component) + ":" + std::string(name); return false; } if (tensor->type != GGML_TYPE_F32) { reason = "neural tensor is not F32: " + std::string(component) + ":" + std::string(name); return false; } output.resize(static_cast(ggml_nelements(tensor))); ggml_backend_tensor_get(tensor, output.data(), 0, output.size() * sizeof(float)); return true; } #else class neural_runtime {}; mb_status create_neural_runtime(const std::filesystem::path &, mb_device, std::uint32_t, const std::string &, std::shared_ptr & output, std::string & reason) { output.reset(); reason = "this build has no GGML support"; return MB_BACKEND_UNAVAILABLE; } #endif } // namespace motionbricks::detail