androidkotlinopengl-es

GLSL Wallpaper Service Showing Black Screen


I'm working on an Android project where I want to set a GLSL shader as the wallpaper using a WallpaperService. The goal is to display a simple gray color shader, but I'm encountering an issue where the wallpaper is just showing a black screen. note.

I am aware of the existence of Shader Editor but I have some new ideas that can make the process of creating and customizing shaders extremely easier.

You can view this for the most basic stripped down version highlighting the problem. Here's the setup:

GLSL code:

    void main() {// basic code for testing purpose
        if(gl_FragCoord.y > 250.0){
            gl_FragColor = vec4(0.25);
        }
        else{
            gl_FragColor = vec4(0.75);
        }
    }

WallpaperService Implementation:

    import android.service.wallpaper.WallpaperService
    import android.view.SurfaceHolder
    import com.dhruv.hellohome.glsl.MyGLSurfaceView
    
    class GLSLWallpaperService : WallpaperService() {
        private var engine: GLWallpaperEngine? = null
    
        fun isRunning(): Boolean {
            return engine != null
        }
    
        override fun onCreateEngine(): Engine {
            return GLWallpaperEngine()
        }
    
        inner class GLWallpaperEngine : Engine() {
            private var glSurfaceView: MyGLSurfaceView? = null
    
            override fun onCreate(surfaceHolder: SurfaceHolder) {
                super.onCreate(surfaceHolder)
                glSurfaceView = MyGLSurfaceView(this@GLSLWallpaperService)
            }
    
            override fun onSurfaceCreated(holder: SurfaceHolder) {
                super.onSurfaceCreated(holder)
                glSurfaceView?.surfaceCreated(holder)
            }
    
            override fun onVisibilityChanged(visible: Boolean) {
                super.onVisibilityChanged(visible)
                if (visible) {
                    glSurfaceView?.onResume()
                } else {
                    glSurfaceView?.onPause()
                }
            }
    
            override fun onDestroy() {
                super.onDestroy()
                glSurfaceView?.let {
                    it.onPause()
                    it.onDetachedFromWindow()
                }
            }
    
            override fun onSurfaceDestroyed(holder: SurfaceHolder) {
                super.onSurfaceDestroyed(holder)
                glSurfaceView?.surfaceDestroyed(holder)
            }
        }
    }

GLSurfaceView Implementation:

    import android.content.Context
    import android.opengl.GLSurfaceView
    import android.util.Log
    import android.view.SurfaceHolder

    class MyGLSurfaceView(context: Context) : GLSurfaceView(context) {
    private val renderer: MyGLRenderer

    init {
        setEGLContextClientVersion(2)
        renderer = MyGLRenderer(ShaderData())
        setRenderer(renderer)
        renderMode = RENDERMODE_CONTINUOUSLY
    }

    override fun surfaceCreated(holder: SurfaceHolder) {
        super.surfaceCreated(holder)
        Log.d("MyGLSurfaceView", "Surface created")
    }

    override fun surfaceDestroyed(holder: SurfaceHolder) {
        super.surfaceDestroyed(holder)
        Log.d("MyGLSurfaceView", "Surface destroyed")
    }

    public override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
    }
}

in this case the MyGLRenderer is an implementation of GLSurfaceView.Renderer that works fine. The minifest is :

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:installLocation="auto">
    <supports-screens
        android:largeScreens="true"
        android:xlargeScreens="true"/>
    <uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="19"/>
    <!-- Required below API < 28 for exporting shaders -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28"/>
    <uses-feature android:glEsVersion="0x00020000"/>
    <uses-feature android:name="android.hardware.camera.any"
        android:required="false"/>
    <uses-feature android:name="android.hardware.camera.autofocus"
        android:required="false"/>
    <uses-feature android:name="android.software.live_wallpaper"/>
    <queries>
        <intent>
            <action android:name="android.intent.action.VIEW"/>
            <data android:scheme="https"/>
        </intent>
    </queries>
    <application
        tools:ignore="UnusedAttribute"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.HelloHome">
        <service
            android:name=".services.GLSLWallpaperService"
            android:exported="true"
            android:description="@string/wallpaper_description"
            android:label="@string/app_name"
            android:permission="android.permission.BIND_WALLPAPER">
            <intent-filter>
                <action android:name="android.service.wallpaper.WallpaperService" />
            </intent-filter>
            <meta-data
                android:name="android.service.wallpaper"
                android:resource="@xml/wallpaper" />
        </service>
        <activity android:name="com.example.openglwallpaper.SettingsActivity" />
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.HelloHome">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".PreviewActivity" />
    </application>
</manifest>

and finally GLSLWallpaperService Implementation:

import android.service.wallpaper.WallpaperService
import android.view.SurfaceHolder
import com.dhruv.hellohome.glsl.MyGLSurfaceView

class GLSLWallpaperService : WallpaperService() {

    private var engine: GLWallpaperEngine? = null
    fun isRunning(): Boolean {
        return engine != null
    }

    override fun onCreateEngine(): Engine {
        return GLWallpaperEngine()
    }

    override fun onDestroy() {
        super.onDestroy()
    }

    inner class GLWallpaperEngine : Engine() {
        private var glSurfaceView: MyGLSurfaceView? = null
        override fun onCreate(surfaceHolder: SurfaceHolder) {
            super.onCreate(surfaceHolder)
            glSurfaceView = MyGLSurfaceView(this@GLSLWallpaperService)
        }

        override fun onSurfaceCreated(holder: SurfaceHolder) {
            super.onSurfaceCreated(holder)
            glSurfaceView?.surfaceCreated(holder)
        }

        override fun onVisibilityChanged(visible: Boolean) {
            super.onVisibilityChanged(visible)
            if (visible) {
                glSurfaceView?.onResume()
            } else {
                glSurfaceView?.onPause()
            }
        }

        override fun onDestroy() {
            super.onDestroy()
            glSurfaceView?.let {
                it.onPause()
                it.onDetachedFromWindow()
            }
        }

        override fun onSurfaceDestroyed(holder: SurfaceHolder) {
            super.onSurfaceDestroyed(holder)
            glSurfaceView?.surfaceDestroyed(holder)
        }
    }
}

I have been stuck for about 2 weeks and just can't seem to understand what is wrong. if any one can help me that would be a huge help.


Solution

  • Ok so the problem was in how GLSLWallpaperService handled engien.

    this is the fixed code

    class GLSLWallpaperService : WallpaperService() {
    
        private var engine: GLWallpaperEngine? = null
    
        fun isRunning(): Boolean {
            return engine != null
        }
    
        override fun onCreateEngine(): Engine {
            engine = GLWallpaperEngine()
            return engine!!
        }
    
        override fun onDestroy() {
            super.onDestroy()
            engine = null
        }
    
        inner class GLWallpaperEngine : Engine() {
            private var glSurfaceView: MyGLSurfaceView? = null
    
            override fun onCreate(surfaceHolder: SurfaceHolder) {
                super.onCreate(surfaceHolder)
                glSurfaceView = GLWallpaperSurfaceView()
            }
    
            override fun onSurfaceCreated(holder: SurfaceHolder) {
                super.onSurfaceCreated(holder)
                glSurfaceView?.surfaceCreated(holder)
            }
    
            override fun onVisibilityChanged(visible: Boolean) {
                super.onVisibilityChanged(visible)
                if (visible) {
                    glSurfaceView?.onResume()
                } else {
                    glSurfaceView?.onPause()
                }
            }
    
            override fun onDestroy() {
                super.onDestroy()
                glSurfaceView?.let {
                    it.onPause()
                    it.onDetachedFromWindow()
                }
            }
    
            override fun onSurfaceDestroyed(holder: SurfaceHolder) {
                super.onSurfaceDestroyed(holder)
                glSurfaceView?.surfaceDestroyed(holder)
            }
    
            inner class GLWallpaperSurfaceView : MyGLSurfaceView(this@GLSLWallpaperService) {
                override fun getHolder(): SurfaceHolder  {
                    return getSurfaceHolder()
                }
            }
        }
    }
    

    it was a stupid question but I hope if anyone else is stuck on it then this might help you.