From 6830c61e317526de0608bbb370233637369da39e Mon Sep 17 00:00:00 2001 From: omigamedev Date: Sun, 28 Dec 2025 21:46:18 +0100 Subject: [PATCH] client egl context and viewport in kotlin UI --- src/main/cpp/CMakeLists.txt | 7 +- src/main/cpp/egl_context.cpp | 57 ++++++++++++--- src/main/cpp/egl_context.h | 9 ++- src/main/cpp/mosis-test.cpp | 70 +++++++++++++++++++ .../java/com/omixlab/mosis/MainActivity.kt | 47 ++++++++++++- 5 files changed, 175 insertions(+), 15 deletions(-) diff --git a/src/main/cpp/CMakeLists.txt b/src/main/cpp/CMakeLists.txt index cbc1c53..e363666 100644 --- a/src/main/cpp/CMakeLists.txt +++ b/src/main/cpp/CMakeLists.txt @@ -1,6 +1,9 @@ cmake_minimum_required(VERSION 3.22.1) project("mosis") +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(ANDROID_SDK "/Users/omar/Library/Android/sdk") set(BINDER_DIR "${ANDROID_SDK}/platforms/android-36/optional/libbinder_ndk_cpp") @@ -14,7 +17,7 @@ add_library(mosis-service SHARED target_include_directories(mosis-service PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${BINDER_DIR} glad/include) target_link_libraries(mosis-service - android log binder_ndk) + android log binder_ndk EGL GLESv2) add_library(mosis-test SHARED com/omixlab/mosis/IMosisService.cpp @@ -27,4 +30,4 @@ add_library(mosis-test SHARED target_include_directories(mosis-test PUBLIC ${CMAKE_CURRENT_LIST_DIR} ${BINDER_DIR} glad/include) target_link_libraries(mosis-test - android log binder_ndk) + android log binder_ndk EGL GLESv2) diff --git a/src/main/cpp/egl_context.cpp b/src/main/cpp/egl_context.cpp index f1e439a..668d453 100644 --- a/src/main/cpp/egl_context.cpp +++ b/src/main/cpp/egl_context.cpp @@ -2,8 +2,10 @@ #include "logger.h" #include #include +#include +#include -bool egl::Context::create() +bool egl::Context::create(ANativeWindow* window) { int version = gladLoaderLoadEGL(EGL_NO_DISPLAY); if (version == 0) @@ -12,7 +14,7 @@ bool egl::Context::create() return false; } if (m_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - m_display == EGL_NO_DISPLAY) + m_display == EGL_NO_DISPLAY) { Logger::Log("eglGetDisplay failed"); return false; @@ -24,14 +26,22 @@ bool egl::Context::create() return false; } version = gladLoaderLoadEGL(EGL_DEFAULT_DISPLAY); - const EGLint config_attribs[] = + std::vector config_attribs = { - EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, - EGL_NONE, }; + if (window) + { + config_attribs.append_range(std::to_array({ + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + })); + } + config_attribs.push_back(EGL_NONE); EGLint num_configs; - if (!eglChooseConfig(m_display, config_attribs, &m_config, 1, &num_configs) || num_configs == 0) + if (!eglChooseConfig(m_display, config_attribs.data(), &m_config, 1, &num_configs) || num_configs == 0) { Logger::Log("eglChooseConfig failed"); return false; @@ -48,11 +58,22 @@ bool egl::Context::create() Logger::Log("eglCreateContext failed"); return false; } - if (!eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_context)) + if (window) + { + m_surface = eglCreateWindowSurface(m_display, m_config, window, nullptr); + m_native_window = window; + } + if (!eglMakeCurrent(m_display, m_surface, m_surface, m_context)) { Logger::Log("eglMakeCurrent failed"); return false; } + int gl_version = gladLoaderLoadGLES2(); + if (gl_version == 0) + { + Logger::Log("glad failed to load GLES2"); + return false; + } return true; } @@ -61,18 +82,36 @@ void egl::Context::destroy() if (m_display != EGL_NO_DISPLAY) { eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (m_surface != EGL_NO_SURFACE) + { + eglDestroySurface(m_display, m_surface); + } if (m_context != EGL_NO_CONTEXT) { eglDestroyContext(m_display, m_context); } + if (m_native_window) + { + ANativeWindow_release(m_native_window); + } eglTerminate(m_display); } m_display = EGL_NO_DISPLAY; m_context = EGL_NO_CONTEXT; + m_config = EGL_NO_CONFIG_KHR; + m_surface = EGL_NO_SURFACE; + m_native_window = nullptr; } -egl::Context::Context() - : m_display(EGL_NO_DISPLAY), m_config(EGL_NO_CONFIG_KHR), m_context(EGL_NO_CONTEXT) +egl::Context::Context() : + m_display(EGL_NO_DISPLAY), m_config(EGL_NO_CONFIG_KHR), + m_context(EGL_NO_CONTEXT), m_surface(EGL_NO_SURFACE), + m_native_window(nullptr) { Logger::Log("egl::Context::Context"); } + +void egl::Context::swap() +{ + eglSwapBuffers(m_display, m_surface); +} diff --git a/src/main/cpp/egl_context.h b/src/main/cpp/egl_context.h index b34c9e6..a1b3a7f 100644 --- a/src/main/cpp/egl_context.h +++ b/src/main/cpp/egl_context.h @@ -2,7 +2,9 @@ typedef void* EGLContext; typedef void* EGLDisplay; +typedef void* EGLSurface; typedef void* EGLConfig; +struct ANativeWindow; namespace egl { @@ -10,10 +12,13 @@ class Context { EGLContext m_context; EGLDisplay m_display; - EGLConfig m_config = nullptr; + EGLSurface m_surface; + EGLConfig m_config; + ANativeWindow* m_native_window; public: Context(); - bool create(); + bool create(ANativeWindow* window = nullptr); void destroy(); + void swap(); }; } diff --git a/src/main/cpp/mosis-test.cpp b/src/main/cpp/mosis-test.cpp index 26bc943..8774f5e 100644 --- a/src/main/cpp/mosis-test.cpp +++ b/src/main/cpp/mosis-test.cpp @@ -1,11 +1,61 @@ #include #include #include +#include #include "logger.h" +#include "egl_context.h" +#include +#include +#include using namespace aidl::com::omixlab::mosis; +class Renderer +{ + std::unique_ptr m_egl_context; + std::thread m_render_loop; + bool m_active = false; + void render_loop(ANativeWindow* window) + { + m_egl_context = std::make_unique(); + if (m_egl_context->create(window)) + { + m_active = true; + while (m_active) + { + render_frame(); + } + } + else + { + Logger::Log("Failed to create EGL context"); + } + } + void render_frame() + { + glClearColor(0.f, 0.5f, 0.5f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + m_egl_context->swap(); + } +public: + bool create(ANativeWindow* window) + { + m_render_loop = std::thread(&Renderer::render_loop, this, window); + return true; + } + void destroy() + { + m_active = false; + if (m_render_loop.joinable()) + m_render_loop.join(); + m_egl_context->destroy(); + m_egl_context.reset(); + } +}; + std::shared_ptr g_service; +std::unique_ptr g_renderer; + extern "C" JNIEXPORT void JNICALL Java_com_omixlab_mosis_MainActivity_serviceConnected(JNIEnv *env, jobject thiz, jobject binder) { @@ -14,3 +64,23 @@ Java_com_omixlab_mosis_MainActivity_serviceConnected(JNIEnv *env, jobject thiz, g_service = IMosisService::fromBinder(spBinder); Logger::Log("Service Connected"); } + +extern "C" +JNIEXPORT void JNICALL +Java_com_omixlab_mosis_MainActivity_setSurface(JNIEnv *env, jobject thiz, jobject surface) { + Logger::Log("setSurface"); + if (!g_renderer) + { + g_renderer = std::make_unique(); + g_renderer->create(ANativeWindow_fromSurface(env, surface)); + } +} +extern "C" +JNIEXPORT void JNICALL +Java_com_omixlab_mosis_MainActivity_destroySurface(JNIEnv *env, jobject thiz) { + Logger::Log("destroySurface"); + if (g_renderer) + { + g_renderer->destroy(); + } +} diff --git a/src/main/java/com/omixlab/mosis/MainActivity.kt b/src/main/java/com/omixlab/mosis/MainActivity.kt index dbfe384..e7e1712 100644 --- a/src/main/java/com/omixlab/mosis/MainActivity.kt +++ b/src/main/java/com/omixlab/mosis/MainActivity.kt @@ -6,7 +6,11 @@ import android.content.ServiceConnection import android.os.Bundle import android.os.IBinder import android.util.Log +import android.view.Surface +import android.view.SurfaceView +import android.view.SurfaceHolder import androidx.activity.ComponentActivity +import androidx.activity.addCallback import androidx.activity.enableEdgeToEdge import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Arrangement @@ -16,7 +20,9 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Button import androidx.compose.material3.Text +import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.viewinterop.AndroidView class MainActivity : ComponentActivity() { init { @@ -25,7 +31,7 @@ class MainActivity : ComponentActivity() { var remote_service: IMosisService? = null var statusText = mutableStateOf("Status: idle") var buttonText = mutableStateOf("Init OS") - private val connection = object : ServiceConnection { + val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { Log.d("MosisTest", "Service Connected") statusText.value = "Service Connected" @@ -39,6 +45,27 @@ class MainActivity : ComponentActivity() { statusText.value = "Service Disconnected" } } + val viewport = object : SurfaceHolder.Callback { + override fun surfaceCreated(holder: SurfaceHolder) { + Log.d("MosisTest", "surfaceCreated") + setSurface(holder.surface) + } + override fun surfaceChanged( + holder: SurfaceHolder, + format: Int, + width: Int, + height: Int + ) { + // Optional: Pass resize events to native if needed + // resizeSurface(width, height) + Log.d("MosisTest", "surfaceResized") + + } + override fun surfaceDestroyed(holder: SurfaceHolder) { + destroySurface() + Log.d("MosisTest", "surfaceDestroyed") + } + } fun initOS() { remote_service?.let { service -> @@ -47,7 +74,7 @@ class MainActivity : ComponentActivity() { buttonText.value = if (result) "OS Initialized" else "OS Not Initialized" } } - private fun startRemoteService() { + fun startRemoteService() { val intent = Intent("com.omixlab.mosis.SERVICE") intent.setPackage("com.omixlab.mosis") try { @@ -73,8 +100,24 @@ class MainActivity : ComponentActivity() { Text(buttonText.value) } Text(statusText.value) + NativeViewport( + modifier = Modifier.fillMaxSize(0.8f) + ) } } } + @Composable + fun NativeViewport(modifier: Modifier = Modifier) { + AndroidView( + modifier = modifier, + factory = { context -> + SurfaceView(context).apply { + holder.addCallback(viewport) + } + } + ) + } external fun serviceConnected(binder: IBinder) + external fun setSurface(surface: Surface) + external fun destroySurface() } \ No newline at end of file