public abstract class

GlProgram

extends java.lang.Object

 java.lang.Object

↳androidx.camera.effects.opengl.GlProgram

Gradle dependencies

compile group: 'androidx.camera', name: 'camera-effects', version: '1.5.0-alpha01'

  • groupId: androidx.camera
  • artifactId: camera-effects
  • version: 1.5.0-alpha01

Artifact androidx.camera:camera-effects:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

A base class that represents an OpenGL program.

Summary

Methods
protected voidconfigure()

Configures this program.

protected voidrelease()

Deletes this program and clears the state.

protected final voiduse()

Uses this program.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Methods

protected void configure()

Configures this program.

This base method configures attributes that are common to all programs. Each subclass should override this method to add its own attributes.

protected final void use()

Uses this program.

protected void release()

Deletes this program and clears the state.

Subclasses should override this method to delete their own resources.

Source

/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.camera.effects.opengl;

import static androidx.camera.effects.opengl.Utils.checkGlErrorOrThrow;
import static androidx.camera.effects.opengl.Utils.checkLocationOrThrow;
import static androidx.camera.effects.opengl.Utils.createFloatBuffer;
import static androidx.core.util.Preconditions.checkState;

import android.opengl.GLES20;

import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.camera.core.Logger;

import java.nio.FloatBuffer;

/**
 * A base class that represents an OpenGL program.
 */
public abstract class GlProgram {

    private static final String TAG = "GlProgram";

    static final String POSITION_ATTRIBUTE = "aPosition";
    static final String TEXTURE_ATTRIBUTE = "aTextureCoord";
    static final String TEXTURE_COORDINATES = "vTextureCoord";
    static final String INPUT_SAMPLER = "samplerInputTexture";

    // Used with {@Link #POSITION_ATTRIBUTE}
    private static final FloatBuffer VERTEX_BUFFER = createFloatBuffer(new float[]{
            -1.0f, -1.0f, // 0 bottom left
            1.0f, -1.0f, // 1 bottom right
            -1.0f, 1.0f, // 2 top left
            1.0f, 1.0f   // 3 top right
    });

    // Used with {@Link #TEXTURE_ATTRIBUTE}
    private static final FloatBuffer TEXTURE_BUFFER = createFloatBuffer(new float[]{
            0.0f, 0.0f, // 0 bottom left
            1.0f, 0.0f, // 1 bottom right
            0.0f, 1.0f, // 2 top left
            1.0f, 1.0f  // 3 top right
    });

    // The size of VERTEX_BUFFER and TEXTURE_BUFFER.
    static final int VERTEX_SIZE = 4;

    int mProgramHandle = -1;

    private final String mVertexShader;
    private final String mFragmentShader;

    GlProgram(@NonNull String vertexShader, @NonNull String programShader) {
        mVertexShader = vertexShader;
        mFragmentShader = programShader;
    }

    /**
     * Initializes this program.
     */
    void init() {
        checkState(!isInitialized(), "Program already initialized.");
        mProgramHandle = createProgram(mVertexShader, mFragmentShader);
        use();
        configure();
    }

    /**
     * Configures this program.
     *
     * <p>This base method configures attributes that are common to all programs. Each subclass
     * should override this method to add its own attributes.
     */
    @CallSuper
    protected void configure() {
        checkInitialized();

        // Configure the vertex of the 3D object (a quadrilateral).
        int positionLoc = GLES20.glGetAttribLocation(mProgramHandle, POSITION_ATTRIBUTE);
        checkLocationOrThrow(positionLoc, POSITION_ATTRIBUTE);
        GLES20.glEnableVertexAttribArray(positionLoc);
        checkGlErrorOrThrow("glEnableVertexAttribArray");
        int coordsPerVertex = 2;
        int vertexStride = 0;
        GLES20.glVertexAttribPointer(positionLoc, coordsPerVertex, GLES20.GL_FLOAT, false,
                vertexStride, VERTEX_BUFFER);
        checkGlErrorOrThrow("glVertexAttribPointer");

        // Configure the coordinate of the texture.
        int texCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, TEXTURE_ATTRIBUTE);
        checkLocationOrThrow(texCoordLoc, TEXTURE_ATTRIBUTE);
        GLES20.glEnableVertexAttribArray(texCoordLoc);
        checkGlErrorOrThrow("glEnableVertexAttribArray");
        int coordsPerTex = 2;
        int texStride = 0;
        GLES20.glVertexAttribPointer(texCoordLoc, coordsPerTex, GLES20.GL_FLOAT, false,
                texStride, TEXTURE_BUFFER);
        checkGlErrorOrThrow("glVertexAttribPointer");
    }

    /**
     * Uses this program.
     */
    @CallSuper
    protected final void use() {
        checkInitialized();
        GLES20.glUseProgram(mProgramHandle);
        checkGlErrorOrThrow("glUseProgram");
    }

    /**
     * Deletes this program and clears the state.
     *
     * <p>Subclasses should override this method to delete their own resources.
     */
    @CallSuper
    protected void release() {
        if (isInitialized()) {
            GLES20.glDeleteProgram(mProgramHandle);
            checkGlErrorOrThrow("glDeleteProgram");
            mProgramHandle = -1;
        }
    }

    private void checkInitialized() {
        checkState(isInitialized(), "Program not initialized");
    }

    private boolean isInitialized() {
        return mProgramHandle != -1;
    }

    private int createProgram(String vertexShaderStr, String fragmentShaderStr) {
        int vertexShader = -1, fragmentShader = -1, program = -1;
        try {
            program = GLES20.glCreateProgram();
            checkGlErrorOrThrow("glCreateProgram");

            vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderStr);
            GLES20.glAttachShader(program, vertexShader);
            checkGlErrorOrThrow("glAttachShader");

            fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderStr);
            GLES20.glAttachShader(program, fragmentShader);
            checkGlErrorOrThrow("glAttachShader");

            GLES20.glLinkProgram(program);
            int[] linkStatus = new int[1];
            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
            if (linkStatus[0] != GLES20.GL_TRUE) {
                throw new IllegalStateException(
                        "Could not link program: " + GLES20.glGetProgramInfoLog(program));
            }
            return program;
        } catch (IllegalStateException | IllegalArgumentException e) {
            if (vertexShader != -1) {
                GLES20.glDeleteShader(vertexShader);
            }
            if (fragmentShader != -1) {
                GLES20.glDeleteShader(fragmentShader);
            }
            if (program != -1) {
                GLES20.glDeleteProgram(program);
            }
            throw e;
        }
    }

    private int loadShader(int shaderType, String source) {
        int shader = GLES20.glCreateShader(shaderType);
        checkGlErrorOrThrow("glCreateShader type=" + shaderType);
        GLES20.glShaderSource(shader, source);
        GLES20.glCompileShader(shader);
        int[] compiled = new int[1];
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Logger.w(TAG, "Could not compile shader: " + source);
            GLES20.glDeleteShader(shader);
            throw new IllegalStateException(
                    "Could not compile shader type " + shaderType + ":" + GLES20.glGetShaderInfoLog(
                            shader)
            );
        }
        return shader;
    }

    @VisibleForTesting
    @NonNull
    String getFragmentShader() {
        return mFragmentShader;
    }
}