Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImGui with NativeActivity on Android question #8444

Open
Duy-Thanh opened this issue Feb 27, 2025 · 0 comments
Open

ImGui with NativeActivity on Android question #8444

Duy-Thanh opened this issue Feb 27, 2025 · 0 comments

Comments

@Duy-Thanh
Copy link

Version/Branch of Dear ImGui:

Version 1.91.8, Branch: release (master/docking/etc.)

Back-ends:

imgui_impl_vulkan.cpp + imgui_impl_android.cpp

Compiler, OS:

Windows 11 with Android Studio 2024.2.2 Patch 2

Full config/build information:

No response

Details:

Hi, I want to ask about the possibility of using ImGui with NativeActivity in Android!

Today when I used ImGui for my project using NativeActivity and Vulkan renderer on Android 15 AVD (API level 35), I set NativeActivity's display style in AndroidManifest.xml to landscape. However, ImGui still seems to try to render the portrait:

Image

Mode text display Landscape, but it displaying in Potrait...

I did everything, including rescaling, but it didn't work....

What did I do wrong? Also, does NativeActivity work with ImGui?

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

And here is the code for ImGui:

// ImGui

bool initImGui(ANativeWindow* window) {
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    
    ImGuiIO& io = ImGui::GetIO();
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
    
    // Get window dimensions and calculate DPI scale
    float windowWidth = ANativeWindow_getWidth(window);
    float windowHeight = ANativeWindow_getHeight(window);
    float dpiScale = windowWidth / 1080.0f; // Base scale on 1080p width
    
    // Adjust scale for different screen sizes
    float baseScale = std::min(windowWidth, windowHeight) / 1080.0f;
    float uiScale = baseScale * 3.0f; // Increased base scaling
    
    io.DisplaySize = ImVec2(windowWidth, windowHeight);
    io.DisplayFramebufferScale = ImVec2(1.0f, 1.0f);
    io.FontGlobalScale = uiScale; // Scale all fonts
    
    // Set style with better visibility
    ImGui::StyleColorsDark();
    ImGuiStyle& style = ImGui::GetStyle();
    
    // Modern dark theme colors
    style.Colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.15f, 0.95f);
    style.Colors[ImGuiCol_TitleBg] = ImVec4(0.15f, 0.15f, 0.20f, 1.00f);
    style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.20f, 0.20f, 0.25f, 1.00f);
    style.Colors[ImGuiCol_Header] = ImVec4(0.20f, 0.20f, 0.25f, 0.55f);
    style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.25f, 0.25f, 0.30f, 0.80f);
    style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.25f, 0.25f, 0.30f, 1.00f);
    style.Colors[ImGuiCol_Text] = ImVec4(0.85f, 0.85f, 0.85f, 1.00f);
    style.Colors[ImGuiCol_Border] = ImVec4(0.30f, 0.30f, 0.35f, 0.50f);
    
    // Adjust style properties
    style.WindowRounding = 12.0f;
    style.FrameRounding = 6.0f;
    style.PopupRounding = 6.0f;
    style.ScrollbarRounding = 6.0f;
    style.GrabRounding = 6.0f;
    style.TabRounding = 6.0f;
    style.WindowBorderSize = 1.0f;
    style.WindowPadding = ImVec2(15.0f, 15.0f);
    style.ItemSpacing = ImVec2(8.0f, 8.0f);
    style.ItemInnerSpacing = ImVec2(6.0f, 6.0f);
    style.FramePadding = ImVec2(6.0f, 4.0f);
    
    // Scale everything
    style.ScaleAllSizes(uiScale);
    
    // Initialize implementations
    if (!ImGui_ImplAndroid_Init(window)) {
        LOGE("Failed to initialize ImGui Android implementation");
        return false;
    }

    // Initialize Vulkan implementation
    ImGui_ImplVulkan_InitInfo init_info = {};
    init_info.Instance = g_vulkan->instance;
    init_info.PhysicalDevice = g_vulkan->physicalDevice;
    init_info.Device = g_vulkan->device;
    init_info.QueueFamily = g_vulkan->graphicsQueueFamily;
    init_info.Queue = g_vulkan->graphicsQueue;
    init_info.PipelineCache = VK_NULL_HANDLE;
    init_info.DescriptorPool = g_vulkan->descriptorPool;
    init_info.MinImageCount = 2;
    init_info.ImageCount = g_vulkan->swapchainImages.size();
    init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
    init_info.RenderPass = g_vulkan->renderPass;
    init_info.Allocator = nullptr;

    if (!ImGui_ImplVulkan_Init(&init_info)) {
        LOGE("Failed to initialize ImGui Vulkan implementation");
        return false;
    }

    // Upload fonts
    if (!ImGui_ImplVulkan_CreateFontsTexture()) {
        LOGE("Failed to create ImGui fonts texture");
        return false;
    }
    
    LOGD("ImGui initialized successfully");
    return true;
}

void cleanupImGui() {
    ImGui_ImplVulkan_Shutdown();
    ImGui_ImplAndroid_Shutdown();
    ImGui::DestroyContext();
}

Here is the main function:

void android_main(android_app* app) {
    // Force landscape orientation
    ANativeActivity_setWindowFlags(app->activity, 0x00000400,0x00000800);
    ANativeActivity* activity = app->activity;
    activity->vm->AttachCurrentThread(&activity->env, nullptr);
    activity->env->CallVoidMethod(activity->clazz, 
        activity->env->GetMethodID(activity->env->GetObjectClass(activity->clazz), 
        "setRequestedOrientation", "(I)V"), 
        0); // 0 = SCREEN_ORIENTATION_LANDSCAPE
    
    app->onAppCmd = [](android_app* app, int32_t cmd) {
        switch (cmd) {
            case APP_CMD_INIT_WINDOW:
                LOGD("APP_CMD_INIT_WINDOW started");
                // Window created, initialize Vulkan
                if (app->window != nullptr) {
                    if (!g_vulkan) {
                        g_vulkan = std::make_unique<VulkanContext>();
                        LOGD("VulkanContext created");
                    }
                    
                    if (!createInstance()) {
                        LOGE("Failed to create instance");
                        return;
                    }
                    LOGD("Instance created");

                    if (!createSurface(app->window)) {
                        LOGE("Failed to create surface");
                        return;
                    }
                    LOGD("Surface created");

                    if (!selectPhysicalDevice()) {
                        LOGE("Failed to select physical device");
                        return;
                    }
                    LOGD("Physical device selected");

                    if (!createLogicalDevice()) {
                        LOGE("Failed to create logical device");
                        return;
                    }
                    LOGD("Logical device created");

                    if (!createSwapchain(app->window)) {
                        LOGE("Failed to create swapchain");
                        return;
                    }
                    LOGD("Swapchain created");

                    if (!createDescriptorPool()) {
                        LOGE("Failed to create descriptor pool");
                        return;
                    }
                    LOGD("Descriptor pool created");

                    if (!createRenderPass()) {
                        LOGE("Failed to create render pass");
                        return;
                    }
                    LOGD("Render pass created");

                    if (!createFramebuffers()) {
                        LOGE("Failed to create framebuffers");
                        return;
                    }
                    LOGD("Framebuffers created");

                    if (!createCommandPool()) {
                        LOGE("Failed to create command pool");
                        return;
                    }
                    LOGD("Command pool created");

                    if (!createCommandBuffer()) {
                        LOGE("Failed to create command buffer");
                        return;
                    }
                    LOGD("Command buffer created");

                    if (!initImGui(app->window)) {
                        LOGE("Failed to init ImGui");
                        return;
                    }
                    LOGD("ImGui initialized");
                }
                break;

            case APP_CMD_TERM_WINDOW:
                LOGD("APP_CMD_TERM_WINDOW");
                cleanupImGui();
                g_vulkan.reset();
                break;

            case APP_CMD_GAINED_FOCUS:
                LOGD("APP_CMD_GAINED_FOCUS");
                break;

            case APP_CMD_LOST_FOCUS:
                LOGD("APP_CMD_LOST_FOCUS");
                break;
        }
    };

    app->onInputEvent = [](android_app* app, AInputEvent* event) -> int32_t {
        return ImGui_ImplAndroid_HandleInputEvent(event);
    };

    // Main loop
    while (true) {
        int events;
        android_poll_source* source;
        
        while (ALooper_pollOnce(0, nullptr, &events, (void**)&source) >= 0) {
            if (source != nullptr) {
                source->process(app, source);
            }
            
            if (app->destroyRequested != 0) {
                LOGD("App destroy requested");
                cleanupImGui();
                g_vulkan.reset();
                return;
            }
        }

        // Only render if we have a window and Vulkan is initialized
        if (app->window != nullptr && g_vulkan && g_vulkan->device != VK_NULL_HANDLE) {
            float windowWidth = ANativeWindow_getWidth(app->window);
            float windowHeight = ANativeWindow_getHeight(app->window);
            float baseScale = std::min(windowWidth, windowHeight) / 1080.0f;
            
            ImGui_ImplAndroid_NewFrame();
            ImGui::NewFrame();

            // Debug window
            static bool show_debug_window = true;
            if (show_debug_window) {
                // Fixed landscape layout
                ImVec2 windowSize(windowWidth * 0.3f, windowHeight * 0.8f);
                ImVec2 windowPos(windowWidth * 0.02f, windowHeight * 0.1f);
                
                ImGui::SetNextWindowPos(windowPos);
                ImGui::SetNextWindowSize(windowSize);
                
                ImGui::Begin("Cosmic AI Debug", &show_debug_window, 
                    ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
                
                // Status section
                ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "STATUS");
                ImGui::Separator();
                
                ImGui::Text("FPS: %.1f", ImGui::GetIO().Framerate);
                ImGui::Text("Frame Time: %.3f ms", 1000.0f / ImGui::GetIO().Framerate);
                
                ImGui::Spacing();
                ImGui::Spacing();
                
                // Display section
                ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), "DISPLAY");
                ImGui::Separator();
                
                ImGui::Text("Resolution: %dx%d", (int)windowWidth, (int)windowHeight);
                ImGui::Text("Scale Factor: %.2f", baseScale);
                ImGui::Text("Mode: %s", windowWidth > windowHeight ? "Landscape" : "Portrait");
                
                ImGui::End();
            }

            // Render
            ImGui::Render();
            renderFrame();
            LOGD("Frame rendered");
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant