android下vulkan與opengles紋理互通


  先放demo源碼地址: 06_mediaplayer

  效果圖:

  

  主要幾個點:

  • 用ffmpeg打開rtmp流。
  • 使用vulkan Compute shader處理yuv420P/yuv422P數據格式成rgba.
  • 初始化android surface為vulkan的交換鏈,把如上結果復制到交換鏈上顯示。
  • 如果是opengles surface,如何不通過CPU直接把數據從vulkan復制到opengles里。

  這個demo主要是為了驗證用vulkan做GPGPU處理后,能輸出到vulkan/opengles紋理,后續有可能的話,明年下半年業余時間在這項目用vulkan Compute shader移植GPUImage學習下,設計的運行平台是window/android,你可以在window平台用vscode(安裝相應cmake/C++插件)運行查看調試過程,也可以用android studio打開里面的android文件夾,運行06_mediaplayer查看對應如上效果。  

集成ffmpeg

  window平台自己編譯后放入aoce/thirdparty/ffmpeg/x64中,android 我直接用的這位同學編譯好的,你也可以在下載我整理好的。

  如下目錄放置就好。

  

   cmake里填寫好相關window/android平台邏輯以后,你可以選擇在window端直接用vscode,也可以用MVS打開cmake生成的sln文件,用android studio打開android文件夾,自動Sync gradle引用cmake並編譯好相關C++項目。

  相應ffmpeg播放器FMediaPlayer的實現我參照了android下的MediaPlayer的接口設計,主要是prepare這個接口讓我意識到我在oeip不合理的地方,按照android下的MediaPlayer的接口重新實現了下ffmpeg里播放實現,現在只是簡單走了下流程,后期還需要設計各個狀態的轉化。

用vulkan的Compute shader做GPGPU計算

  這個是我在 Vulkan在Android使用Compute shader 就准備做的事,設想是用有序無環圖來構建GPU計算流程,這個demo也算是簡單驗證下這個流程,后續會驗證設計的多輸入輸出部分,如下構建。  

    // 生成一張執行圖
    vkGraph = AoceManager::Get().getPipeGraphFactory(gpuType)->createGraph();
    auto *layerFactory = AoceManager::Get().getLayerFactory(gpuType);
    inputLayer = layerFactory->crateInput();
    outputLayer = layerFactory->createOutput();
    // 輸出GPU數據
    outputLayer->updateParamet({false, true});
    yuv2rgbLayer = layerFactory->createYUV2RGBA();
    // 生成圖
    vkGraph->addNode(inputLayer)->addNode(yuv2rgbLayer)->addNode(outputLayer);

  其中yuv2rgbLayer現在完成了nv12/yuv420P/yuy422P/yuyvI這些常見格式的解析,貼一段yuv420P的代碼,CS代碼需要配合線程組的划分來看,大家可以在github查看完整流程,YUV轉換的代碼主要注意盡量不要多個線程同時讀或者寫某個地址就好。  

void yuv420p(){
    ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
    ivec2 size = imageSize(outTex);
    if(uv.x >= size.x/2 || uv.y >= size.y/2){
        return;
    }
	ivec2 nuv = u12u2(u22u1(uv.xy, size.x/2), size.x);
	ivec2 uindex = nuv + ivec2(0,size.y);
	ivec2 vindex = nuv + ivec2(0,size.y*5/4);

	float y1 = imageLoad(inTex,ivec2(uv.x*2,uv.y*2)).r;
	float y2 = imageLoad(inTex,ivec2(uv.x*2+1,uv.y*2)).r;
	float y3 = imageLoad(inTex,ivec2(uv.x*2,uv.y*2+1)).r;
	float y4 = imageLoad(inTex,ivec2(uv.x*2+1,uv.y*2+1)).r;	
	float u = imageLoad(inTex,uindex.xy).r -0.5f;
        float v = imageLoad(inTex,vindex.xy).r -0.5f;

	vec4 rgba1 = yuv2Rgb(y1,u,v,1.f);
	vec4 rgba2 = yuv2Rgb(y2,u,v,1.f);
	vec4 rgba3 = yuv2Rgb(y3,u,v,1.f);
	vec4 rgba4 = yuv2Rgb(y4,u,v,1.f);	

	imageStore(outTex, ivec2(uv.x*2,uv.y*2),rgba1); 
	imageStore(outTex, ivec2(uv.x*2+1,uv.y*2),rgba2); 
	imageStore(outTex, ivec2(uv.x*2,uv.y*2+1),rgba3); 
	imageStore(outTex, ivec2(uv.x*2+1,uv.y*2+1),rgba4); 
}

用vulkan顯示

  Vulkan在Android使用Compute shader 里面,我使用native window完成vulkan展示與顯示,但是很多時候,窗口並不是特定的,這個項目的設計目標之一也是無窗口使用Vulkan的Compute shader完成圖像處理,后續高效支持對接android UI/UE4/Unity3d/window UI SDK都方便。

  在這,我們需要查看運行結果,在這我們換種方式,不用native window,不然方便的android UI都不能用了,在native window的實現方式中,我們知道vulkan 交換鏈只需要ANativeWindow,相應的UI消息循環顯示圖像處理結果過程我們並不需要,通過查看 android圖形框架指明了surface對應的C++類就是ANativeWindow,這樣我們可以直接使用surface來完成vulkan 圖像呈現工程,相關實現可以看vulkan模塊下的vulkanwindow里的initsurface實現。

  其中這個demo的window/andorid刷新部分有些不同,window利用本身主線程的窗口空閑時間,把ffmpeg解碼線程上的數據經vulkan處理后復制到交換鏈當前顯示圖像中,而android本身的Surface沒有找到GLSurfaceView.Renderer類似的onDrawFrame時機,所以在android vulkan中,處理與復制呈現給交換鏈全在ffmpeg解碼線程上,不過android 下面的opengles顯示又和window一樣,主線程直接復制呈現,而ffmpeg解碼線程用vulkan處理。

opengles顯示

  本來我想着用vulkan處理了,就能放棄opengl相關,但是至少現在還不現實,android端本身圖像APP,以及對應android上的UE4/Unity3D,opengl es可能是更成熟的選擇。

  如何把vulkan的計算結果直接復制給opengles,通過文檔 android文檔AHardwareBuffer 其中有句話AHardwareBuffers可以綁定到EGL/OpenGL和Vulkan原語,通過相應提示用法VK_ANDROID_external_memory_android_hardware_buffer擴展/eglGetNativeClientBufferANDROID,我們繼續搜索,查找到 google vulkantest 這里有段源碼,詳細說明了如何把vulkan里的vkImage綁定到AHardwareBuffer上,opengles部分根據eglGetNativeClientBufferANDROID查找到如何把AHardwareBuffer綁定到對應的opengles紋理上,這樣我們通過vkImage-AHardwareBuffer-opengl texture的綁定,對應其封裝在aoce_vulkan模塊的hardwareImage類中。

  AHardwareBuffer后續還可以繼續挖,android原生平台提供的多媒體相關的SDK如攝像機,MediaPlay等都有AHardwareBuffer的身影,后續可以嘗試直接把相關綁定到vulkan加快運算。

  本人android開發並不熟悉,歡迎大家指正其中錯誤的地方。

 


免責聲明!

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



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