This commit is contained in:
2026-01-06 17:47:11 +01:00
commit 8423e2b790
1313 changed files with 295355 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1cbd7617bffb546ec80e5c75c643ddc0
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
package com.omixlab.mosis.unity
import android.util.Log
import android.content.ComponentName
import android.content.Context.BIND_AUTO_CREATE
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import androidx.core.content.ContextCompat.startForegroundService
import com.omixlab.mosis.IMosisService
import com.unity3d.player.UnityPlayer
class MyKotlinPlugin {
companion object {
val instance: MyKotlinPlugin by lazy { MyKotlinPlugin() }
@JvmStatic
fun StartMosisService() {
instance.startRemoteService()
}
}
var remote_service: IMosisService? = null
val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
Log.d("MosisTest", "Service Connected")
remote_service = IMosisService.Stub.asInterface(service)
Log.d("MosisTest", "Number: ${remote_service?.number}")
serviceConnected(service)
}
override fun onServiceDisconnected(arg0: ComponentName) {
Log.d("MosisTest", "Service Disconnected")
}
}
fun startRemoteService() {
val intent = Intent("com.omixlab.mosis.SERVICE")
intent.setPackage("com.omixlab.mosis")
val currentActivity = UnityPlayer.currentActivity
try {
startForegroundService(currentActivity, intent)
val result = currentActivity.bindService(intent, connection, BIND_AUTO_CREATE)
Log.d("MosisTest", "Bind result: $result")
} catch (e: Exception) {
Log.e("MosisTest", "Bind failed", e)
}
}
external fun serviceConnected(binder: IBinder)
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 2a4d588f6f5f5424ca8c5e3885c951ba

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0366819e591a440a48281f7386275d17
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1166ea2c496784b4bba8295123b248a7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a615486c1ed22429a9db60a4b567faa2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
package android.hardware;
@JavaOnlyStableParcelable @NdkOnlyStableParcelable parcelable HardwareBuffer ndk_header "android/hardware_buffer_aidl.h";

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 75d8b5cdaa87e4797a7e59862e4d7e89
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b44d8db48a75e488d89f7d7f5839ecb5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ae40e14fe44eb471aba26128ec535b27
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1608a3841bd2a4f2793ace76c2a4582a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,10 @@
package com.omixlab.mosis;
import android.hardware.HardwareBuffer;
// oneway = fire-and-forget (asynchronous)
oneway interface IMosisListener {
void onServiceInitialized(boolean success);
void onBufferAvailable(in HardwareBuffer buffer);
void onFrameAvailable();
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 08472853f0bb04dfb8fb91a8c87d5af5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
package com.omixlab.mosis;
import com.omixlab.mosis.IMosisListener;
interface IMosisService {
boolean initOS(IMosisListener listener);
int getNumber();
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8b212b10f095b40698e0f88dd1642b9c
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e9dff155f6b041d28dc2143160a5684
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.22.1)
project("MyNativePlugin")
find_library(log-lib log)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(PLUGIN_API "/Applications/Unity/Hub/Editor/6000.3.2f1/Unity.app/Contents/PluginAPI")
set(ANDROID_SDK "/Users/omar/Library/Android/sdk")
set(BINDER_DIR "${ANDROID_SDK}/platforms/android-36/optional/libbinder_ndk_cpp")
set(SHARED_SRC_DIR "/Users/omar/Desktop/NativeService/src/main/cpp/")
add_library(my_native_lib SHARED
my_native_code.cpp
${SHARED_SRC_DIR}/com/omixlab/mosis/IMosisService.cpp
${SHARED_SRC_DIR}/com/omixlab/mosis/IMosisListener.cpp
${SHARED_SRC_DIR}/logger.cpp
${SHARED_SRC_DIR}/external_texture.cpp
${SHARED_SRC_DIR}/render_target.cpp
${SHARED_SRC_DIR}/glad/src/egl.c
${SHARED_SRC_DIR}/glad/src/gles2.c
)
target_link_libraries(my_native_lib ${log-lib} binder_ndk EGL GLESv2 nativewindow)
target_include_directories(my_native_lib PUBLIC
${SHARED_SRC_DIR}
${SHARED_SRC_DIR}/glad/include
${BINDER_DIR}
${PLUGIN_API}
)

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 321e7452e1c8c4c74812b77bd3f296d5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 604c411489f9043dcbd071e443f5dc07
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,195 @@
#include <jni.h>
#include <aidl/com/omixlab/mosis/IMosisListener.h>
#include <aidl/com/omixlab/mosis/BnMosisListener.h>
#include <logger.h>
#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>
using namespace aidl::com::omixlab::mosis;
using namespace aidl::android::hardware;
std::shared_ptr<class IMosisService> g_service;
std::shared_ptr<class ServiceContext> g_context;
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);
}
};
typedef void (*OnMessageCallback)(const char*);
typedef void (*OnServiceInitializedCallback)(bool success);
typedef void (*OnFrameAvailableCallback)();
typedef void (*OnBufferReadyCallback)();
typedef void (*OnTextureReadyCallback)(GLuint gl_texture);
struct NativeCallbacks
{
OnMessageCallback OnMessage;
OnServiceInitializedCallback OnServiceInitialized;
OnFrameAvailableCallback OnFrameAvailable;
OnBufferReadyCallback OnBufferReady;
OnTextureReadyCallback OnTextureReady;
};
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);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus onFrameAvailable() override
{
Logger::Log("onFrameAvailable");
g_callbacks.OnMessage("onFrameAvailable");
g_callbacks.OnFrameAvailable();
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus onBufferAvailable(const HardwareBuffer &in_buffer) override
{
Logger::Log("onBufferAvailable");
g_callbacks.OnMessage("onBufferAvailable");
m_hwbuffer = in_buffer.get();
AHardwareBuffer_acquire(m_hwbuffer);
g_callbacks.OnBufferReady();
return ndk::ScopedAStatus::ok();
}
[[nodiscard]] GLuint create_texture()
{
m_texture = std::make_unique<TextureBlitter>();
m_texture->create(m_hwbuffer);
return m_texture->dest_texture_id();
}
void update_texture()
{
if (m_texture)
m_texture->blit();
}
};
extern "C" void SetNativeCallbacks(const NativeCallbacks& ptr)
{
g_callbacks = ptr;
}
extern "C" UnityRenderingEvent InitGLAD()
{
return [](int eventId){
gladLoaderLoadEGL(EGL_NO_DISPLAY);
int egl_version = gladLoadEGL(eglGetCurrentDisplay(), eglGetProcAddress);
if (egl_version == 0)
{
Logger::Log("Failed to load EGL");
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);
}
};
}
extern "C" UnityRenderingEvent UpdateTexture()
{
return [](int eventId){
if (g_context)
{
g_context->update_texture();
}
};
}
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<ServiceContext>();
bool result{};
g_service->initOS(g_context, &result);
Logger::Log(std::format("InitOS returned {}", result));
}

View File

@@ -0,0 +1,54 @@
fileFormatVersion: 2
guid: 2d802269589194242b4f22af14a9bc73
PluginImporter:
externalObjects: {}
serializedVersion: 3
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
Android:
enabled: 0
settings:
AndroidLibraryDependee: UnityLibrary
AndroidSharedLibraryType: Executable
CPU: ARMv7
Any:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux64: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Editor:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
Linux64:
enabled: 0
settings:
CPU: x86_64
OSXUniversal:
enabled: 0
settings:
CPU: None
Win:
enabled: 0
settings:
CPU: x86
Win64:
enabled: 0
settings:
CPU: None
userData:
assetBundleName:
assetBundleVariant: