Vulkan在Android使用Compute shader


  oeip 相關功能只能運行在window平台,想移植到android平台,暫時選擇vulkan做為圖像處理,主要一是里面有單獨的計算管線且支持好,二是熟悉下最新的渲染技術思路。

  這個 demo(git地址) 的功能很簡單,在android下,利用vulkan的compute shader對輸入圖進行1-x的運行后,把計算結果復制到當前交換鏈里正在渲染的圖像上顯示出來。

  

  本文主要記錄其中一些過程,因為第一次嘗試類似開發,所以有誤的地方歡迎大家指出。

  前期准備工作主要如下,VSCode C++環境配置,熟悉CMake。

  為什么選擇vscode,而不是visual studio/android studio,主要是基於如下考慮,首先在win平台方便調試與測試,在win平台完成demo后再移植到android下就方便了,而visual studio/android studio分別在開發原生win/android下方便,而vscode+cmake的組合很方便在win平台調試測試,然后平穩生成相應的android studio項目方案,然后在android studio里進行調試封裝,並且最新android studio首選cmake構建,更方便集成。VSCode必需的C++插件主要是如下幾個 C/C++ for Visual Studio Code/Cmake/Cmake tools.

  然后我花了一些時間在vscode里編譯了ogre-next,並把它的cmake文件跟了一遍,大致了解了cmake的用法,總結了下 CMake常用命令 。

  vulkan結合github上二個vulkan 的demo方案,初步了解vulkan API與渲染流程。vulkan網上說的二千多行代碼畫個三角形確實一點也不誇張,Vulkan API粒度細,控制度高,以及為多線程渲染設計的渲染隊列,渲染命令及同步,所以代碼量看着就上去了,但是你根據你的需求簡單封裝下,如交換鏈,渲染管線,UBO,buffer等,再寫也就大部分業務邏輯代碼。簡單來說,先根據demo熟悉流程與API,再手動寫個2K多行的簡單demo,在這過程,通過比較以前opengl/dx的API流程熟悉與加深思路,然后根據你的需求確定一些參數,封裝一些類,最后開始你的需求並反向不斷完善更新你的封裝庫。

  知乎上各位大佬已經把Vulkan API/Demo講解的非常清晰,這里就說下這個DEMO的流程,供大家參考,歡迎大家指出理解有誤的部分。

  首先窗口初始化相關,這部分也是android/window平台區別最大的部分,注意這里有個大坑,不同win32下,在創建窗口的線程下可以直接創建surface,在android下需要等到窗口的消息APP_CMD_INIT_WINDOW里才能創建surface,android里的native activity初始化過程可以參考 Android——NativeActivity - C/C++ Apk開發,創建surface過程,選擇呈現/渲染通道以及同步呈現與渲染的對象,創建renderpass,然后根據surface創建交換鏈,根據交換鏈得到呈現image列表,根據image列表得到fbo列表用於附着到RenderPass上用於渲染,這里選擇一種比較簡單的CommandBuffer記錄方式,就是有交換鏈有幾個image,就創建幾個對應的CommandBuffer記錄.

  然后創建邏輯設備,加載需要參與計算的輸入圖像資源,一般來說,圖像資源要使用compute shader,usage肯定要有VK_IMAGE_USAGE_STORAGE_BIT,而現在大部分硬件來說,線性 features不支持VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,用於CPU 可以訪問的資源需要linera_tiling.簡單來說,compute shader要求的紋理,現在硬件上,CPU大部分不能直接訪問,這就要求一個中轉,先創建一個CPU可以訪問的資源如vkbuffer,然后把數據導入這個資源中,然后通過設備資源間vkCmdCopyBufferToImage復制到原CPU不能訪問的GPU紋理上。然后創建一個compute shader要求的輸出紋理,對應一個UBO結構,這個UBO對應compute shader輸入輸出。加載轉化的spv文件,生成對應的compute pipeline.

  在這個需求里,渲染命令不會每楨修改,所以我們完全可以在開始楨渲染前就填充CommandBuffer。

  1. 填充計算管線的CommandBuffer,簡單來說,就是執行上面的compute pipeline,並把輸出紋理layout改成VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL。
  2. 填充呈現渲染里的CommandBuffer,根據交換鏈里的image數據填充對應的每個CommandBuffer,簡單來說,就是把上面計算完成的紋理通過vkCmdBlitImage復制到當前呈現的那張vkimage中。

  注意,這里只是保存了動作,相當於把action放入隊列中,並沒執行隊列,在這里,所有在每楨運行前的邏輯已經處理完。

  然后到每楨渲染,如上先等計算管線的fences來信號,這表明GPU隊列中已經執行完成CommandBuffer,如下代碼的computerCmd又變成可執行狀態。注意創建fences時需要先給信號,不然第一次進入就會一起等待,並且fences需要手動reset.然后根據vkAcquireNextImageKHR得到的索引拿到呈現渲染的CommandBuffer,執行完成呈現出來,呈現與渲染的同步都在設備GPU內,一般用VkSemaphore來同步,不同於vkFence,他用於gpu-gpu間的同步,自動reset.  

void onPreDraw() {
    auto device = context->logicalDevice.device;
    vkWaitForFences(device, 1, &computerFence, VK_TRUE, UINT64_MAX);
    vkResetFences(device, 1, &computerFence);

    VkSubmitInfo submitInfo = {};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &context->computerCmd;

    VK_CHECK_RESULT(
        vkQueueSubmit(context->computeQueue, 1, &submitInfo, computerFence));
}

  在window平台測試完,參照vulkan的demo,新建一個android文件夾,設置其中的setings.gragle.

  

  主build.gradle就和一般的一樣,在vkcs1目錄下的build.gradle添加externalNativeBuild的cmake路徑,設置好AndroidManifest.xml,如下圖。

  

  然后就可以用android studio打開這個文件夾,然后Sync Project with Gradle Files,就會補起成余下內容,最后應該是如下結構。

  

  正常來說,應該就可以在android studio安裝及調試了。

  最后說下在移植到android下遇到的一些坑。

  1. undefined symbol: ANativeActivity_onCreate 找不到,解決方法在CMake中添加set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
  2. vkCreateAndroidSurfaceKHR 類似Fatal signal 11 (SIGSEGV)錯誤。這是前面說的因為需要等到APP_CMD_INIT_WINDOW 消息后,才能初始化surface.
  3. 在WIN平台glsl轉的spv文件可以直接在android上使用,而hlsl的不行,這里不知是否有誤,測試不行。
  4. 1080P下16/16的結果不對,二種解決方案,一是使用32/8,滿足整除,但是需要圖像滿足對應的長寬條件,二是divup,然后在shader里傳入width/hight,檢查ThreadID.xy在width/hight范圍了,需要做if檢查,但是不限制圖像長寬大小。

  參考:

   vulkan基本API用法實例

   vulkan進階demo.

   上面vulkan demo的講解。

  Android——NativeActivity - C/C++ Apk開發


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM