#include #include #include #include #include #include #include #include #include "texture_backend.h" #include "opengl_backend.h" #include "vulkan_backend.h" using namespace aidl::com::omixlab::mosis; using namespace aidl::android::hardware; // Global state std::shared_ptr g_service; std::shared_ptr g_context; std::unique_ptr g_backend; IUnityInterfaces* g_unityInterfaces = nullptr; UnityGfxRenderer g_rendererType = kUnityGfxRendererNull; // Callback function pointers typedef void (*OnMessageCallback)(const char*); typedef void (*OnServiceInitializedCallback)(bool success); typedef void (*OnFrameAvailableCallback)(); typedef void (*OnBufferReadyCallback)(); typedef void (*OnTextureReadyCallback)(void* nativeTexturePtr, int width, int height, bool isVulkan); struct NativeCallbacks { OnMessageCallback OnMessage; OnServiceInitializedCallback OnServiceInitialized; OnFrameAvailableCallback OnFrameAvailable; OnBufferReadyCallback OnBufferReady; OnTextureReadyCallback OnTextureReady; }; NativeCallbacks g_callbacks{}; class ServiceContext : public BnMosisListener { AHardwareBuffer* m_hwbuffer = nullptr; public: ndk::ScopedAStatus onServiceInitialized(bool in_success) override { Logger::Log("onServiceInitialized"); if (g_callbacks.OnMessage) g_callbacks.OnMessage("onServiceInitialized"); if (g_callbacks.OnServiceInitialized) g_callbacks.OnServiceInitialized(in_success); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus onFrameAvailable() override { Logger::Log("onFrameAvailable"); if (g_callbacks.OnMessage) g_callbacks.OnMessage("onFrameAvailable"); if (g_callbacks.OnFrameAvailable) g_callbacks.OnFrameAvailable(); return ndk::ScopedAStatus::ok(); } ndk::ScopedAStatus onBufferAvailable(const HardwareBuffer &in_buffer) override { Logger::Log("onBufferAvailable"); if (g_callbacks.OnMessage) g_callbacks.OnMessage("onBufferAvailable"); m_hwbuffer = in_buffer.get(); AHardwareBuffer_acquire(m_hwbuffer); if (g_callbacks.OnBufferReady) g_callbacks.OnBufferReady(); return ndk::ScopedAStatus::ok(); } bool CreateTexture() { if (!m_hwbuffer) { Logger::Log("CreateTexture: No hardware buffer available"); return false; } if (!g_backend) { Logger::Log("CreateTexture: No backend available"); return false; } if (!g_backend->Create(m_hwbuffer)) { Logger::Log("CreateTexture: Backend Create failed"); return false; } // Notify C# with texture info if (g_callbacks.OnTextureReady) { g_callbacks.OnTextureReady( g_backend->GetNativeTexturePtr(), g_backend->GetWidth(), g_backend->GetHeight(), g_backend->IsVulkan() ); } return true; } void UpdateTexture() { if (g_backend) { g_backend->Update(); } } }; // Unity Plugin Interface extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces) { g_unityInterfaces = unityInterfaces; IUnityGraphics* graphics = unityInterfaces->Get(); if (!graphics) { Logger::Log("UnityPluginLoad: IUnityGraphics not available"); return; } g_rendererType = graphics->GetRenderer(); Logger::Log(std::format("UnityPluginLoad: Renderer type = {}", static_cast(g_rendererType))); // Try to create Vulkan backend first if (g_rendererType == kUnityGfxRendererVulkan) { auto vulkanBackend = std::make_unique(); if (vulkanBackend->Initialize(unityInterfaces)) { g_backend = std::move(vulkanBackend); Logger::Log("UnityPluginLoad: Using Vulkan backend"); return; } Logger::Log("UnityPluginLoad: Vulkan initialization failed, falling back to OpenGL"); } // Fall back to OpenGL for GLES renderer (Unity 6+ only supports GLES 3.0+) if (g_rendererType == kUnityGfxRendererOpenGLES30) { g_backend = std::make_unique(); Logger::Log("UnityPluginLoad: Using OpenGL backend"); } else { Logger::Log(std::format("UnityPluginLoad: Unsupported renderer type {}", static_cast(g_rendererType))); } } extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload() { Logger::Log("UnityPluginUnload"); // Clean up backend if (g_backend) { g_backend->Destroy(); g_backend.reset(); } g_unityInterfaces = nullptr; g_rendererType = kUnityGfxRendererNull; } // Native callback setters extern "C" void SetNativeCallbacks(const NativeCallbacks& ptr) { g_callbacks = ptr; } // Touch input forwarding extern "C" void SendTouchDown(float x, float y) { if (g_service) { g_service->onTouchDown(x, y); } } extern "C" void SendTouchMove(float x, float y) { if (g_service) { g_service->onTouchMove(x, y); } } extern "C" void SendTouchUp(float x, float y) { if (g_service) { g_service->onTouchUp(x, y); } } // Unity render thread callbacks static void UNITY_INTERFACE_API OnInitBackendRenderThread(int eventId) { Logger::Log("OnInitBackendRenderThread"); // For OpenGL, we need to initialize GLAD on the render thread if (!g_backend->IsVulkan()) { if (!OpenGLTextureBackend::InitializeGLAD()) { Logger::Log("OnInitBackendRenderThread: Failed to initialize GLAD"); return; } } if (g_context) { g_context->CreateTexture(); } } static void UNITY_INTERFACE_API OnUpdateTextureRenderThread(int eventId) { if (g_context) { g_context->UpdateTexture(); } } extern "C" UnityRenderingEvent InitBackend() { return OnInitBackendRenderThread; } extern "C" UnityRenderingEvent UpdateTexture() { return OnUpdateTextureRenderThread; } // Legacy compatibility - redirect to new names extern "C" UnityRenderingEvent InitGLAD() { return InitBackend(); } // JNI entry point from Kotlin extern "C" JNIEXPORT void JNICALL Java_com_omixlab_mosis_unity_MyKotlinPlugin_serviceConnected(JNIEnv *env, jobject thiz, jobject binder) { AIBinder* pBinder = AIBinder_fromJavaBinder(env, binder); const ndk::SpAIBinder spBinder(pBinder); g_service = IMosisService::fromBinder(spBinder); Logger::Log("Service Connected"); g_context = ndk::SharedRefBase::make(); bool result{}; g_service->initOS(g_context, &result); Logger::Log(std::format("InitOS returned {}", result)); }