[英]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.