【问题标题】:Can we use Vulkan with Java Activity on Android platform我们可以在 Android 平台上将 Vulkan 与 Java Activity 一起使用吗
【发布时间】:2017-07-18 05:07:47
【问题描述】:

目前,似乎所有 Vulkan 教程和示例都使用 Android 平台上的 NativeActivity。我想知道我们是否可以在 Android 上将 Vulkan 与 Java Activity 一起使用?

【问题讨论】:

  • 你的意思是在纯Java项目中使用Vulkan吗?如果是这样,不确定。如果您的意思是在调用本机库(c/c++ 代码)的 Java Activity 中使用 Vulkan 进行渲染,那么是的,这是可能的——我在一些已发布的标题中这样做。不幸的是,我不能提供详细信息,也不知道有任何示例显示如何设置它。
  • lwjgl 有一个 vulkan 包装器,虽然我不确定 android compat
  • 感谢您的建议。

标签: android vulkan


【解决方案1】:

假设您有一个封装了 Vulkan 绘图逻辑的 C++ 类:

// File: AndroidGraphicsApplication.hpp

#include <android/asset_manager.h>
#include <android/native_window.h>
#include "GraphicsApplication.h" // Base class shared with iOS/macOS/...

class AndroidGraphicsApplication : public GraphicsApplication {

public:
    AndroidGraphicsApplication(AAssetManager* assetManager, ANativeWindow* window): GraphicsApplication() {
        mAssetManager = assetManager;
        mWindow = window;
        // ... Vulkan initialisation code.
    }
    ~AndroidGraphicsApplication() {
        // ... Vulkan cleanup code.
    }

    void createSurface() {
       VkAndroidSurfaceCreateInfoKHR surface_info;
       surface_info.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
       surface_info.pNext = NULL;
       surface_info.flags = 0;
       surface_info.window = mWindow;
       if(vkCreateAndroidSurfaceKHR(instance, &surface_info, NULL, &surface) != VK_SUCCESS) {
          throw std::runtime_error("failed to create window surface!");
       }
    }

    // Used to setup shaders.
    std::vector<char> readFile(const std::string& filename) {
       AAsset* file = AAssetManager_open(mAssetManager, filename.c_str(), AASSET_MODE_BUFFER);
       size_t size = AAsset_getLength(file);
       std::vector<char> data(size);
       AAsset_read(file, data.data(), size);
       AAsset_close(file);
       return data;
    }

    void setSize(uint32_t w, uint32_t h) {
       width = w;
       height = h;
    }

private:
    AAssetManager* mAssetManager;
    ANativeWindow* mWindow;
    uint32_t width;
    uint32_t height;
};

你有如下的 JNI 桥:

// File: VulkanAppBridge.cpp

#include <android/log.h>
#include <android/native_window_jni.h>
#include <android/asset_manager_jni.h>
#include "AndroidGraphicsApplication.hpp"

AndroidGraphicsApplication *mApplicationInstance = NULL;

extern "C" {

   JNIEXPORT void JNICALL
   Java_com_mc_demo_vulkan_VulkanAppBridge_nativeCreate(JNIEnv *env, jobject vulkanAppBridge,
         jobject surface, jobject pAssetManager) {
      if (mApplicationInstance) {
         delete mApplicationInstance;
         mApplicationInstance = NULL;
      }
      __android_log_print(ANDROID_LOG_DEBUG, "mc-native-VulkanAppBridge", "create");
      auto window = ANativeWindow_fromSurface(env, surface);
      auto assetManager = AAssetManager_fromJava(env, pAssetManager);
      mApplicationInstance = new AndroidGraphicsApplication(assetManager, window);
   }

   JNIEXPORT void JNICALL
   Java_com_mc_demo_vulkan_VulkanAppBridge_nativeDestroy(JNIEnv *env, jobject vulkanAppBridge) {
      __android_log_print(ANDROID_LOG_DEBUG, "mc-native-VulkanAppBridge", "destroy");
      if (mApplicationInstance) {
         delete mApplicationInstance;
         mApplicationInstance = NULL;
      }
   }

   JNIEXPORT void JNICALL
   Java_com_mc_demo_vulkan_VulkanAppBridge_nativeResize(JNIEnv *env, jobject vulkanAppBridge, jint width, jint height) {
      __android_log_print(ANDROID_LOG_DEBUG, "mc-native-VulkanAppBridge", "resize: %dx%d", width, height);
      if (mApplicationInstance) {
         mApplicationInstance->setSize(width, height);
         mApplicationInstance->isResizeNeeded = true;
      }
   }

   JNIEXPORT void JNICALL
   Java_com_mc_demo_vulkan_VulkanAppBridge_nativeDraw(JNIEnv *env, jobject vulkanAppBridge) {
      __android_log_print(ANDROID_LOG_DEBUG, "mc-native-VulkanAppBridge", "draw");
      if (mApplicationInstance) {
         mApplicationInstance->drawFrame();
      }
   }
}

你有一个对应的 JNI 桥的 Java/Kotlin 部分:

// File: VulkanAppBridge.kt

class VulkanAppBridge {

   init {
      System.loadLibrary("myApplication")
   }

   private external fun nativeCreate(surface: Surface, assetManager: AssetManager)
   private external fun nativeDestroy()
   private external fun nativeResize(width: Int, height: Int)
   private external fun nativeDraw()

   fun create(surface: Surface, assetManager: AssetManager) {
      nativeCreate(surface, assetManager)
   }

   fun destroy() {
      nativeDestroy()
   }

   fun resize(width: Int, height: Int) {
      nativeResize(width, height)
   }

   fun draw() {
      nativeDraw()
   }
}

你有一个SurfaceView 的自定义子类:

// File: VulkanSurfaceView.kt

class VulkanSurfaceView: SurfaceView, SurfaceHolder.Callback2 {

    private var vulkanApp = VulkanAppBridge()

    constructor(context: Context): super(context) {
    }

    constructor(context: Context, attrs: AttributeSet): super(context, attrs) {
    }

    constructor(context: Context, attrs: AttributeSet, defStyle: Int): super(context, attrs, defStyle) {
    }

    constructor(context: Context, attrs: AttributeSet, defStyle: Int, defStyleRes: Int): super(context, attrs, defStyle, defStyleRes) {
    }

    init {
        alpha = 1F
        holder.addCallback(this)
    }

    // ...
    // Implementation code similar to one in GLSurfaceView is skipped.
    // See: https://android.googlesource.com/platform/frameworks/base/+/master/opengl/java/android/opengl/GLSurfaceView.java
    // ...

    override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
        vulkanApp.resize(width, height)
    }

    override fun surfaceDestroyed(holder: SurfaceHolder?) {
        vulkanApp.destroy()
    }

    override fun surfaceCreated(holder: SurfaceHolder?) {
        holder?.let { h ->
            vulkanApp.create(h.surface, resources.assets)
        }
    }

    override fun surfaceRedrawNeeded(holder: SurfaceHolder?) {
        vulkanApp.draw()
    }
}

然后您可以将自定义大小的自定义VulkanSurfaceView 内部布局与其他视图一起使用:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id= "@+id/linearlayout1" >

    <Button
        android:id="@+id/mcButtonTop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A Button" />

    <com.mc.demo.vulkan.MyGLSurfaceView
        android:id="@+id/mcSurfaceView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0.23" />

    <!-- Custom SurfaceView -->
    <com.mc.demo.vulkan.VulkanSurfaceView
        android:id="@+id/mcVulkanSurfaceView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0.23" />

    <Button
        android:id="@+id/mcButtonBottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="A Button" />

</LinearLayout>

结果:

这里是“Vulkan 案例研究”的链接,其中包含 Android 使用示例:https://www.khronos.org/assets/uploads/developers/library/2016-vulkan-devu-seoul/2-Vulkan-Case-Study.pdf

【讨论】:

    【解决方案2】:

    是的,您可以将 Vulkan 与您自己的 Activity 子类一起使用。由于 Android 没有针对 Vulkan 的 Java 语言绑定,因此您需要使用 JNI 或第三方 Java Vulkan 库(它只是为您执行 JNI)。

    您的视图层次结构将需要包含SurfaceView,并且当您获得 Surfaceholder.Callback#surfaceChanged 回调时,您可以获得 Surface。如果您自己做 JNI,您可以调用 ANativeWindow_fromSurface 从 Surface 获取 ANativeWindow,并使用它来创建您的 VkSurfaceKHR/VkSwapchainKHR。

    需要注意的一件事是在调用 VkAcquireNextImageKHR 时避免阻塞主 UI 线程。要么安排你只在它不会长时间阻塞时调用它,要么把你的帧循环放在一个单独的线程上。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-03
      • 2012-04-25
      • 2022-01-04
      • 2012-07-13
      • 1970-01-01
      • 2021-06-02
      • 2019-06-17
      • 1970-01-01
      相关资源
      最近更新 更多