glsurfaceview是啥东西

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了glsurfaceview是啥东西相关的知识,希望对你有一定的参考价值。

参考技术A  android游戏当中主要的除了控制类外就是显示类View。SurfaceView是从View基类中派生出来的显示类。android游戏开发中常用的三种视图是:  view、SurfaceView和GLSurfaceView的区别如下:  View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢。  SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快。  GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类,专用于3D游戏开发的视图;是SurfaceView的子类,openGL专用。  在2D游戏开发中,大致可以分为两种游戏框架,View和SurfaceView。  View和SurfaceView区别:  View:必须在UI的主线程中更新画面,用于被动更新画面。  surfaceView:UI线程和子线程中都可以。在一个新启动的线程中重新绘制画面,主动更新画面。  UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。  当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步,涉及到线程同步。  所以基于以上,根据游戏特点,一般分成两类。  1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。  2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

如何设置 GLSurfaceView 不清理我以前画过的东西?

【中文标题】如何设置 GLSurfaceView 不清理我以前画过的东西?【英文标题】:How to set GLSurfaceView not clean things I've draw before? 【发布时间】:2015-07-29 06:41:18 【问题描述】:

我试图在 android 中使用 opengl es 2.0。我使用 GLSurfaceView 作为我的主要视图。这是问题。我想连续画几个点。也就是当我点击视图时,我画了一个点并调用requestRender。但我不想存储我触发的所有点,因为它们会很多。所以我的onDrawFrame()只有一句话,大概是这样的:

GLES20.glDrawArrays(GLES20.GL_POINTS, 8, 1);

问题是,这个逻辑在我的 android 虚拟机上运行良好,但是当我画一个新点时,我画的所有东西都消失了。

有没有办法保留我在 GLSurfaceView 上绘制的所有点,然后继续绘制而不保存所有点?

【问题讨论】:

【参考方案1】:

您需要每帧重绘整个屏幕。输出通常是双缓冲或三缓冲的,因此您不能依赖先前内容的可用性。始终以glClear() 呼叫开始。

执行您想要的操作的一种方法是渲染到 FBO,然后将 FBO blit 到屏幕上。您可以在 Grafika 的“record GL app”活动中找到一个示例,该活动实际上正在使用它,因此它可以将每个帧渲染两次(一次到屏幕,一次到视频编码器),并在网络上的各种示例中。基本思想是您渲染到 GLES 纹理上,而不是直接渲染到 Surface 中,因此无需应对双缓冲。

【讨论】:

【参考方案2】:

如果您在onFrameRedraw() 方法中使用了GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT),请将其删除,然后在渲染器的onSurfaceCreated() 方法中使用它。

【讨论】:

【参考方案3】:

我还在努力禁用双缓冲或三缓冲。最后,我找到了解决方案。 (参见Automatic buffer clear while using OpenGL on Android)

    原则上,此类配置与 OpenGL (ES) 无关。这是 EGL 的。 有一个名为EGL_BUFFER_PRESERVED 的配置可以启用附加绘图。 要使用此配置,您可以选择直接使用SurfaceView 而不是GLSurfaceView 来完全控制EGL,如@fadden 建议的above。或者, 您仍然可以使用 GLSurfaceViewsetEGLConfigChooser 自定义 EGLConfigChooser 对象,然后在适当的时间调用 EGL14.eglSurfaceAttrib

自定义 EGLConfigChooser 类:

启用EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT

/**
 * This class will choose a RGB_888 surface with or without a depth buffer.
 * (Choosing a RGB_888 with a depth buffer is GLSurfaceView's default behavior.)
 *
 * In addition to the default behavior, it will enable EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT
 * of EGL10.EGL_SURFACE_TYPE.
 *
 * cf. https://www.khronos.org/registry/EGL/sdk/docs/man/html/eglChooseConfig.xhtml
 */
class SimpleConfigChooser(
    private val eglContextClientVersion: Int, withDepthBuffer: Boolean = true,
) : GLSurfaceView.EGLConfigChooser 

    private val value = IntArray(1)

    private val redSize = 8
    private val greenSize = 8
    private val blueSize = 8
    private val alphaSize = 0
    private val depthSize = if (withDepthBuffer) 16 else 0
    private val stencilSize = 0

    private val configSpec = filterConfigSpec(intArrayOf(
        EGL10.EGL_RED_SIZE, redSize,
        EGL10.EGL_GREEN_SIZE, greenSize,
        EGL10.EGL_BLUE_SIZE, blueSize,
        EGL10.EGL_ALPHA_SIZE, alphaSize,
        EGL10.EGL_DEPTH_SIZE, depthSize,
        EGL10.EGL_STENCIL_SIZE, stencilSize,
        EGL10.EGL_SURFACE_TYPE, (EGL10.EGL_WINDOW_BIT or EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT),
        EGL10.EGL_NONE
    ))

    private fun filterConfigSpec(configSpec: IntArray): IntArray 
        if (eglContextClientVersion != 2 && eglContextClientVersion != 3) 
            return configSpec
        
        /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
         * And we know the configSpec is well formed.
         */
        val len = configSpec.size
        val newConfigSpec = IntArray(len + 2)
        System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1)
        newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE
        if (eglContextClientVersion == 2) 
            newConfigSpec[len] = EGL14.EGL_OPENGL_ES2_BIT /* EGL_OPENGL_ES2_BIT */
         else 
            newConfigSpec[len] = EGLExt.EGL_OPENGL_ES3_BIT_KHR /* EGL_OPENGL_ES3_BIT_KHR */
        
        newConfigSpec[len + 1] = EGL10.EGL_NONE
        return newConfigSpec
    

    override fun chooseConfig(egl: EGL10, display: EGLDisplay): EGLConfig 
        val numConfig = IntArray(1)
        require(egl.eglChooseConfig(
            display, configSpec, null, 0, numConfig
        ))  "eglChooseConfig#1/2 failed" 

        val numConfigs = numConfig[0]
        require(numConfigs > 0)  "No configs match configSpec" 

        val configs = arrayOfNulls<EGLConfig>(numConfigs)
        require(egl.eglChooseConfig(
            display, configSpec, configs, numConfigs, numConfig
        ))  "eglChooseConfig#2/2 failed" 

        return chooseConfig(egl, display, configs)
            ?: throw IllegalArgumentException("No config chosen")
    

    private fun chooseConfig(
        egl: EGL10, display: EGLDisplay, configs: Array<EGLConfig?>,
    ): EGLConfig? 
        for (config in configs) 
            if (config == null) 
                continue
            
            val d: Int = findConfigAttrib(egl, display, config,
                EGL10.EGL_DEPTH_SIZE, 0)
            val s: Int = findConfigAttrib(egl, display, config,
                EGL10.EGL_STENCIL_SIZE, 0)
            if (d >= depthSize && s >= stencilSize) 
                val r: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_RED_SIZE, 0)
                val g: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_GREEN_SIZE, 0)
                val b: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_BLUE_SIZE, 0)
                val a: Int = findConfigAttrib(egl, display, config,
                    EGL10.EGL_ALPHA_SIZE, 0)
                if (r == redSize && g == greenSize
                    && b == blueSize && a == alphaSize
                ) 
                    return config
                
            
        
        return null
    

    private fun findConfigAttrib(
        egl: EGL10, display: EGLDisplay, config: EGLConfig, attribute: Int, defaultValue: Int,
    ): Int 
        return if (egl.eglGetConfigAttrib(display, config, attribute, value)) 
            value[0]
         else defaultValue
    

在您的 GLSurfaceView.Renderer 子类中:

调用EGL14.eglSurfaceAttrib

override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) 
    EGL14.eglSurfaceAttrib(
        EGL14.eglGetCurrentDisplay(),
        EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW),
        EGL14.EGL_SWAP_BEHAVIOR, EGL14.EGL_BUFFER_PRESERVED
    )

    // some other work...

主活动:

将自定义EGLConfigChooser 设置为GLSurfaceView

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)

    val binding = MainActivityBinding.inflate(layoutInflater)

    val EGL_CONTEXT_CLIENT_VERSION = 2

    binding.glSurfaceView.setEGLContextClientVersion(
        EGL_CONTEXT_CLIENT_VERSION
    )
    binding.glSurfaceView.setEGLConfigChooser(
        SimpleConfigChooser(EGL_CONTEXT_CLIENT_VERSION)
    )
    binding.glSurfaceView.setRenderer(Renderer()) // this should be the last

    setContentView(binding.root)

    // some other work...

【讨论】:

以上是关于glsurfaceview是啥东西的主要内容,如果未能解决你的问题,请参考以下文章

一文了解surface,surfaceview,surfaceTexture,GLsurfaceView

TextureView 与 GLSurfaceView 或如何将 GLSurfaceView 与 EGL14 一起使用

GLSurfaceView的简单分析及巧妙借用

调整大小后 GLSurfaceView 相机预览被打乱

GLSurfaceView 如何与surfaceflinger 连接?

android-glsurfaceview Activity框架程序