This commit is contained in:
2026-01-17 08:46:11 +01:00
parent 0aea07026d
commit e7a514a713
10 changed files with 1371 additions and 126 deletions

View File

@@ -5,90 +5,29 @@
#include <aidl/com/omixlab/mosis/IMosisService.h>
#include <android/binder_ibinder_jni.h>
#include <format>
#include <glad/gles2.h>
#include <glad/egl.h>
#include <IUnityGraphics.h>
#include <external_texture.h>
#include <render_target.h>
#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<IMosisService> g_service;
std::shared_ptr<class ServiceContext> g_context;
std::unique_ptr<ITextureBackend> g_backend;
IUnityInterfaces* g_unityInterfaces = nullptr;
UnityGfxRenderer g_rendererType = kUnityGfxRendererNull;
class TextureBlitter
{
GLuint source_texture = 0;
GLuint source_fb = 0;
GLuint dest_texture = 0;
GLuint dest_fb = 0;
GLint width;
GLint height;
public:
bool create(AHardwareBuffer* hardwareBuffer)
{
AHardwareBuffer_Desc desc{};
AHardwareBuffer_describe(hardwareBuffer, &desc);
width = desc.width;
height = desc.height;
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(hardwareBuffer);
EGLImageKHR eglImage = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, clientBuffer, nullptr);
if (eglImage == EGL_NO_IMAGE_KHR)
{
Logger::Log("Failed to create EGL image");
return false;
}
glGenTextures(1, &source_texture);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, source_texture);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glGenFramebuffers(1, &source_fb);
glBindFramebuffer(GL_FRAMEBUFFER, source_fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_EXTERNAL_OES, source_texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
return false;
glGenTextures(1, &dest_texture);
glBindTexture(GL_TEXTURE_2D, dest_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, desc.width, desc.height,
0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
glGenFramebuffers(1, &dest_fb);
glBindFramebuffer(GL_FRAMEBUFFER, dest_fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, dest_texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
return false;
blit();
return true;
}
[[nodiscard]] GLuint dest_texture_id() const
{
return dest_texture;
}
void blit()
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, source_fb);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dest_fb);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
};
// Callback function pointers
typedef void (*OnMessageCallback)(const char*);
typedef void (*OnServiceInitializedCallback)(bool success);
typedef void (*OnFrameAvailableCallback)();
typedef void (*OnBufferReadyCallback)();
typedef void (*OnTextureReadyCallback)(GLuint gl_texture);
typedef void (*OnTextureReadyCallback)(void* nativeTexturePtr, int width, int height, bool isVulkan);
struct NativeCallbacks
{
OnMessageCallback OnMessage;
@@ -102,49 +41,156 @@ NativeCallbacks g_callbacks{};
class ServiceContext : public BnMosisListener
{
AHardwareBuffer* m_hwbuffer = nullptr;
std::unique_ptr<TextureBlitter> m_texture;
public:
ndk::ScopedAStatus onServiceInitialized(bool in_success) override
{
Logger::Log("onServiceInitialized");
g_callbacks.OnMessage("onServiceInitialized");
g_callbacks.OnServiceInitialized(in_success);
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");
g_callbacks.OnMessage("onFrameAvailable");
g_callbacks.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");
g_callbacks.OnMessage("onBufferAvailable");
if (g_callbacks.OnMessage)
g_callbacks.OnMessage("onBufferAvailable");
m_hwbuffer = in_buffer.get();
AHardwareBuffer_acquire(m_hwbuffer);
g_callbacks.OnBufferReady();
if (g_callbacks.OnBufferReady)
g_callbacks.OnBufferReady();
return ndk::ScopedAStatus::ok();
}
[[nodiscard]] GLuint create_texture()
bool CreateTexture()
{
m_texture = std::make_unique<TextureBlitter>();
m_texture->create(m_hwbuffer);
return m_texture->dest_texture_id();
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 update_texture()
void UpdateTexture()
{
if (m_texture)
m_texture->blit();
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<IUnityGraphics>();
if (!graphics)
{
Logger::Log("UnityPluginLoad: IUnityGraphics not available");
return;
}
g_rendererType = graphics->GetRenderer();
Logger::Log(std::format("UnityPluginLoad: Renderer type = {}", static_cast<int>(g_rendererType)));
// Try to create Vulkan backend first
if (g_rendererType == kUnityGfxRendererVulkan)
{
auto vulkanBackend = std::make_unique<VulkanTextureBackend>();
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 renderers
if (g_rendererType == kUnityGfxRendererOpenGLES20 ||
g_rendererType == kUnityGfxRendererOpenGLES30)
{
g_backend = std::make_unique<OpenGLTextureBackend>();
Logger::Log("UnityPluginLoad: Using OpenGL backend");
}
else
{
Logger::Log(std::format("UnityPluginLoad: Unsupported renderer type {}", static_cast<int>(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)
@@ -153,40 +199,60 @@ extern "C" void SendTouchMove(float x, float y)
}
}
extern "C" UnityRenderingEvent InitGLAD()
extern "C" void SendTouchUp(float x, float y)
{
return [](int eventId){
gladLoaderLoadEGL(EGL_NO_DISPLAY);
int egl_version = gladLoadEGL(eglGetCurrentDisplay(), eglGetProcAddress);
if (egl_version == 0)
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("Failed to load EGL");
Logger::Log("OnInitBackendRenderThread: Failed to initialize GLAD");
return;
}
int gl_version = gladLoaderLoadGLES2();
if (gl_version == 0)
{
Logger::Log("Failed to load GL");
return;
}
if (g_context)
{
GLuint texture = g_context->create_texture();
g_callbacks.OnTextureReady(texture);
}
};
}
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 [](int eventId){
if (g_context)
{
g_context->update_texture();
}
};
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,
@@ -196,6 +262,7 @@ Java_com_omixlab_mosis_unity_MyKotlinPlugin_serviceConnected(JNIEnv *env, jobjec
const ndk::SpAIBinder spBinder(pBinder);
g_service = IMosisService::fromBinder(spBinder);
Logger::Log("Service Connected");
g_context = ndk::SharedRefBase::make<ServiceContext>();
bool result{};
g_service->initOS(g_context, &result);