簡體   English   中英

如何為Android的SurfaceTexture實現Glium的后端(Render with Rust into SurfaceTexture)

[英]How to implement Glium's Backend for Android's SurfaceTexture (Render with Rust into SurfaceTexture)

Rust 的glium庫是一個很好的 OpenGL 包裝器,可以方便地進行插槽。 為了為其實現新的后端,您必須實現https://github.com/glium/glium/blob/cacb970c8ed2e45a6f98d12bd7fcc03748b0e122/src/backend/mod.rs#L36

我想實現 Android 的 SurfaceTexture 作為Backend

看起來我需要為 SurfaceTexture 實現一個新的后端: https ://github.com/glium/glium/blob/master/src/backend/mod.rs#L36

以下是 SurfaceTexture 的 C++ 函數https://developer.android.com/ndk/reference/group/surface-texture#summary

我認為Backend::make_current(&self); 映射到ASurfaceTexture_attachToGLContext ( ASurfaceTexture *st, uint32_t texName)

並且Backend::is_current(&self) -> bool可以基於每個SurfaceTexture在調用時被標記為活動或不活動以某種方式進行模擬。

也許Backend::get_framebuffer_dimensions(&self) -> (u32, u32)是在創建時定義的 SurfaceTexture 的大小,所以我可以使用它。 我只是不知道如何處理Backend::swap_buffers(&self) -> Result<(), SwapBuffersError>

也許Backend::unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void可以調用一些獲取 OpenGL 函數地址的 Android API

但是, ASurfaceTexture_updateTexImage(ASurfaceTexture *st)看起來很重要且需要,我不知道在Backend中將其映射到什么。 另外, ASurfaceTexture_detachFromGLContext(ASurfaceTexture *st)怎么樣?

PS:我知道還有其他方法可以渲染到 android 小部件,但我需要渲染到 Flutter 小部件,並且唯一的方法是通過 SurfaceTexture

前段時間我設法通過一個 hack-ish 解決方案完成了這項工作,也許它仍然有效,因為glium最近變化不大。

但是根據我使用ASurfaceTexture的經驗,結果不可靠,可能是我用錯了,也可能是Android廠商沒有太在意,我不知道。 但是我沒有看到任何真正的程序使用它,所以我決定改用經過良好測試的 Java GLSurfaceView和一點 JNI 來連接所有東西。

class MyGLView extends GLSurfaceView
                implements GLSurfaceView.Renderer {
    public MyGLView(Context context) {
        super(context);
        setEGLContextClientVersion(2);
        setEGLConfigChooser(8, 8, 8, 0, 0, 0);
        setRenderer(this);
    }
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLJNILib.init();
    }
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLJNILib.resize(width, height);
    }
    public void onDrawFrame(GL10 gl) {
        GLJNILib.render();
}

com.example.myapp.GLJNILib是 JNI 綁定到 Rust 本地庫的地方,魔法發生的地方。 界面非常簡單:

package com.example.myapplication;

public class GLJNILib {

     static {
        System.loadLibrary("myrustlib");
     }

     public static native void init();
     public static native void resize(int width, int height);
     public static native void step();
}

現在,可以通過多種方式設計這個 Rust 庫。 在我的特定項目中,因為它是一個具有單個全屏視圖的簡單游戲,所以我只是創建了glium上下文並將其存儲在一個全局變量中。 更復雜的程序可以將Backend存儲到 Java 對象中,但這會使生命周期復雜化,我不需要它。

struct Data {
    dsp: Rc<glium::backend::Context>,
    size: (u32, u32),
}

static mut DATA: Option<Data> = None;

但首先我們必須實現 trait glium::backend::Backend ,如果我們假設每次調用一個 Rust 函數時,正確的 GL 上下文總是最新的,那么這恰好是非常容易的:

struct Backend;

extern "C" {
    fn eglGetProcAddress(procname: *const c_char) -> *const c_void;
}

unsafe impl glium::backend::Backend for Backend {
    fn swap_buffers(&self) -> Result<(), glium::SwapBuffersError> {
        Ok(())
    }
    unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
        let cs = CString::new(symbol).unwrap();
        let ptr = eglGetProcAddress(cs.as_ptr());
        ptr
    }
    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        let data = unsafe { &DATA.as_ref().unwrap() };
        data.size
    }
    fn is_current(&self) -> bool {
        true
    }
    unsafe fn make_current(&self) {
    }
}

現在我們可以實現 JNI 的init函數了:

use jni::{
    JNIEnv,
    objects::{JClass, JObject},
    sys::{jint}
};

#[no_mangle]
#[allow(non_snake_case)]
pub extern "system"
fn Java_com_example_myapp_GLJNILib_init(_env: JNIEnv, _class: JClass) { log_panic(|| {
    unsafe {
        DATA = None
    };

    let backend = Backend;
    let dsp = unsafe { glium::backend::Context::new(backend, false, Default::default()).unwrap() };
    // Use dsp to create additional GL objects: programs, textures, buffers...
    // and store them inside `DATA` or another global.
    unsafe {
        DATA = Some(Data {
            dsp,
            size: (256, 256), //dummy size
        });
    }
}

當視圖的大小發生變化時,大小將被更新(不是glium使用該值這么多):

#[no_mangle]
#[allow(non_snake_case)]
pub extern "system"
fn Java_com_example_myapp_GLJNILib_resize(_env: JNIEnv, _class: JClass, width: jint, height: jint) {
    let data = unsafe { &mut DATA.as_mut().unwrap() };
    data.size = (width as u32, height as u32);
}

和類似的render功能:

#[no_mangle]
#[allow(non_snake_case)]
pub extern "system"
fn Java_com_example_myapp_GLJNILib_render(_env: JNIEnv, _class: JClass) {
    let data = unsafe { &mut DATA.as_ref().unwrap() };
    let dsp = &data.dsp;

    let mut target = glium::Frame::new(dsp.clone(), dsp.get_framebuffer_dimensions());
    
    // use dsp and target at will, such as:
    target.clear_color(0.0, 0.0, 1.0, 1.0);
    let (width, height) = target.get_dimensions();
    //...

    target.finish().unwrap();
}

請注意,盡管glium實際上並沒有進行交換,但仍然需要target.finish()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM