Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determining Max/Min Texture Size Limit in Android OpenGLES

I've been assigned to create an open source Java port of this Objective C GPUImage Framework so that it can be used in an Android application. I am to recreate it as closely as I can, with all the variable names, function names, etc all the same. I'm in the beginning stages and I'm trying to port GPUImageOpenGLESContext.h and GPUImageOpenGLESContext.m (Sorry, would provide links, but as a new users I cannot add any more links).

I'm having difficulty with these methods

+ (GLint)maximumTextureSizeForThisDevice;
{
    GLint maxTextureSize; 
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
    return maxTextureSize;
}

+ (GLint)maximumTextureUnitsForThisDevice;
{
    GLint maxTextureUnits; 
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
    return maxTextureUnits;
}

It seems that in Objective C, you can just simply call these methods but in Java you cannot. I've done some searching and found that most people said to use a GLSurfaceView, but that would require an activity, correct? I was very excited when I found this Get Maximum OpenGL ES 2.0 Texture Size Limit on Android, but the response claims that the code would not work.

So, my question is, how can I get the minimum and maximum texture in a class that is not an activity? Using GLSurfaceView?

I'd also appreciate any suggestions on how to port this over. I've never ported anything from Objective C to Java, so any advice would be appreciated!

If it would be helpful, here is my current code:

public class GPUImageOpenGLESContext 
{
    private static GPUImageOpenGLESContext instance = null;

    EGLContext context;

    protected GPUImageOpenGLESContext()
    {
        // This is a protected empty method
        // that exists only to prevent
        // this singleton object from
        // multiple instantiation

        return;
    }

    public enum GPUImageRotationMode { 
            kGPUImageNoRotation, kGPUImageRotateLeft, kGPUImageRotateRight, kGPUImageFlipVertical,
            kGPUImageFlipHorizontal, kGPUImageRotateRightFlipVertical, kGPUImageRotate180
    }

    public GPUImageRotationMode GPUImageRotationSwapsWidthAndHeight(GPUImageRotationMode rotation)
    {
        // TODO: Implement GPUImageRotationSwapsWidthAndHeight macro as method
        //rotation = ((rotation) == kGPUImageRotateLeft || (rotation) == kGPUImageRotateRight || (rotation) == kGPUImageRotateRightFlipVertical)
        return rotation;
    }

    public static GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext()
    {
        if (instance == null)
        {
            instance = new GPUImageOpenGLESContext();
        }
        return instance;
    }

    public static void useImageProcessingContext()
    {
         EGLContext imageProcessingContext = GPUImageOpenGLESContext.sharedImageProcessingOpenGLESContext().context;
         if (EGLContext.getEGL() != imageProcessingContext)
         {
             // In Objective C, this call would be here:
             // [EAGLContext setCurrentContext:imageProcessingContext]

             // Cannot figure out how to handle this.  For now, throws an exception.
             throw new RuntimeException("useImageProcessingContext not equal to EGLContext");
         }

         return;
    }

    public static int maximumTextureSizeForThisDevice()
    {
        int[] maxTextureSize = new int[1];

        // TODO: See if you can use gl. without an activity
        //GL10 gl = new GL10();
        //EGL gl = EGLContext.getEGL();

        //gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);

        return maxTextureSize[0];
    }

    public static int maximumTextureUnitsForThisDevice()
    {
        // TODO: Implement maximumTextureUnitsForThisDevice();
        return -1;
    }

    public static CGSize sizeThatFitsWithinATextureForSize(CGSize inputSize)
    {
        int maxTextureSize = maximumTextureSizeForThisDevice();

        if ((inputSize.width < maxTextureSize) && (inputSize.height < maxTextureSize))
        {
            return inputSize;
        }

        CGSize adjustedSize = new CGSize();
        if (inputSize.width > inputSize.height)
        {
            adjustedSize.width = (float)maxTextureSize;
            adjustedSize.height = ((float)maxTextureSize / inputSize.width) * inputSize.height;
        }
        else
        {
            adjustedSize.height = (float)maxTextureSize;
            adjustedSize.width = ((float)maxTextureSize / inputSize.height) * inputSize.width;
        }

        return adjustedSize;
    }

    public EGLContext getContext()
    {
        if (context == null)
        {
            // TODO: Implement getContext()
        }
    }

    public interface GPUImageInput
    {
        public void newFrameReadyAtTime(Time frameTime);
        public void setInputTextureAtIndex(int newInputTexture, int textureIndex);
        public int nextAvailableTextureIndex();
        public void setInputSizeAtIndex(CGSize newSize, int textureIndex);
        public void setInputRotationAtIndex(GPUImageRotationMode newInputRotation, int textureIndex);
        public CGSize maximumOutputSize();
        public void endProcessing();
        public boolean shouldIgnoreUpdatesToThisTarget();
    }
}
like image 440
Synergy807 Avatar asked Jul 11 '12 22:07

Synergy807


1 Answers

I realize this is an old post but your problem is that you haven't properly initialized the EGLContext.

Usually you'd want to use a GLSurfaceView or a TextureView to actually include your GL content in the View hierarchy. The GLSurfaceView will handle a lot of things for you like properly creating the EGLContext and managing a render thread. The TextureView requires a bit more manual work.

Once you have a context through either of these means, you can use :

GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, size, 0);

Assuming you have bound the OpenGL ES 2.0 API. First make sure that you have properly created your EGLContext and can execute EGL and GLES calls then you should be able to query the max texture size.

You can see Romain Guy's post about using a TextureView as you would a GLSurfaceView to see the nitty gritty details about managing your own EGLContext here (https://groups.google.com/d/msg/android-developers/U5RXFGpAHPE/IqHeIeGXhr0J):

GLSurfaceView handles GL setup for you, which TextureView will not do. A TextureView can be used as the native window when you create an EGL surface. Here is an example (the interesting part is the call to eglCreateWindowSurface()):

@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    mRenderThread = new RenderThread(getResources(), surface);
    mRenderThread.start();
}

private static class RenderThread extends Thread {
    private static final String LOG_TAG = "GLTextureView";

    static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
    static final int EGL_OPENGL_ES2_BIT = 4;

    private volatile boolean mFinished;

    private final Resources mResources;
    private final SurfaceTexture mSurface;

    private EGL10 mEgl;
    private EGLDisplay mEglDisplay;
    private EGLConfig mEglConfig;
    private EGLContext mEglContext;
    private EGLSurface mEglSurface;
    private GL mGL;

    RenderThread(Resources resources, SurfaceTexture surface) {
        mResources = resources;
        mSurface = surface;
    }

    private static final String sSimpleVS =
            "attribute vec4 position;\n" +
            "attribute vec2 texCoords;\n" +
            "varying vec2 outTexCoords;\n" +
            "\nvoid main(void) {\n" +
            "    outTexCoords = texCoords;\n" +
            "    gl_Position = position;\n" +
            "}\n\n";
    private static final String sSimpleFS =
            "precision mediump float;\n\n" +
            "varying vec2 outTexCoords;\n" +
            "uniform sampler2D texture;\n" +
            "\nvoid main(void) {\n" +
            "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
            "}\n\n";

    private static final int FLOAT_SIZE_BYTES = 4;
    private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
    private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
    private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
    private final float[] mTriangleVerticesData = {
            // X, Y, Z, U, V
            -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
             1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
            -1.0f,  1.0f, 0.0f, 0.0f, 1.0f,
             1.0f,  1.0f, 0.0f, 1.0f, 1.0f,
    };

    @Override
    public void run() {
        initGL();

        FloatBuffer triangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length
                * FLOAT_SIZE_BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();
        triangleVertices.put(mTriangleVerticesData).position(0);

        int texture = loadTexture(R.drawable.large_photo);
        int program = buildProgram(sSimpleVS, sSimpleFS);

        int attribPosition = glGetAttribLocation(program, "position");
        checkGlError();

        int attribTexCoords = glGetAttribLocation(program, "texCoords");
        checkGlError();

        int uniformTexture = glGetUniformLocation(program, "texture");
        checkGlError();

        glBindTexture(GL_TEXTURE_2D, texture);
        checkGlError();

        glUseProgram(program);
        checkGlError();

        glEnableVertexAttribArray(attribPosition);
        checkGlError();

        glEnableVertexAttribArray(attribTexCoords);
        checkGlError();

        glUniform1i(uniformTexture, texture);
        checkGlError();

        while (!mFinished) {
            checkCurrent();

            glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            checkGlError();

            glClear(GL_COLOR_BUFFER_BIT);
            checkGlError();

            // drawQuad
            triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
            glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);

            triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
            glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);

            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

            if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
                throw new RuntimeException("Cannot swap buffers");
            }
            checkEglError();

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // Ignore
            }
        }

        finishGL();
    }

    private int loadTexture(int resource) {
        int[] textures = new int[1];

        glActiveTexture(GL_TEXTURE0);
        glGenTextures(1, textures, 0);
        checkGlError();

        int texture = textures[0];
        glBindTexture(GL_TEXTURE_2D, texture);
        checkGlError();

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        Bitmap bitmap = BitmapFactory.decodeResource(mResources, resource);

        GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
        checkGlError();

        bitmap.recycle();

        return texture;
    }

    private int buildProgram(String vertex, String fragment) {
        int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
        if (vertexShader == 0) return 0;

        int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
        if (fragmentShader == 0) return 0;

        int program = glCreateProgram();
        glAttachShader(program, vertexShader);
        checkGlError();

        glAttachShader(program, fragmentShader);
        checkGlError();

        glLinkProgram(program);
        checkGlError();

        int[] status = new int[1];
        glGetProgramiv(program, GL_LINK_STATUS, status, 0);
        if (status[0] != GL_TRUE) {
            String error = glGetProgramInfoLog(program);
            Log.d(LOG_TAG, "Error while linking program:\n" + error);
            glDeleteShader(vertexShader);
            glDeleteShader(fragmentShader);
            glDeleteProgram(program);
            return 0;
        }

        return program;
    }

    private int buildShader(String source, int type) {
        int shader = glCreateShader(type);

        glShaderSource(shader, source);
        checkGlError();

        glCompileShader(shader);
        checkGlError();

        int[] status = new int[1];
        glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
        if (status[0] != GL_TRUE) {
            String error = glGetShaderInfoLog(shader);
            Log.d(LOG_TAG, "Error while compiling shader:\n" + error);
            glDeleteShader(shader);
            return 0;
        }

        return shader;
    }

    private void checkEglError() {
        int error = mEgl.eglGetError();
        if (error != EGL10.EGL_SUCCESS) {
            Log.w(LOG_TAG, "EGL error = 0x" + Integer.toHexString(error));
        }
    }

    private void checkGlError() {
        int error = glGetError();
        if (error != GL_NO_ERROR) {
            Log.w(LOG_TAG, "GL error = 0x" + Integer.toHexString(error));
        }
    }

    private void finishGL() {
        mEgl.eglDestroyContext(mEglDisplay, mEglContext);
        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
    }

    private void checkCurrent() {
        if (!mEglContext.equals(mEgl.eglGetCurrentContext()) ||
                !mEglSurface.equals(mEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
                throw new RuntimeException("eglMakeCurrent failed "
                        + GLUtils.getEGLErrorString(mEgl.eglGetError()));
            }
        }
    }

    private void initGL() {
        mEgl = (EGL10) EGLContext.getEGL();

        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
        if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay failed "
                    + GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }

        int[] version = new int[2];
        if (!mEgl.eglInitialize(mEglDisplay, version)) {
            throw new RuntimeException("eglInitialize failed " +
                    GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }

        mEglConfig = chooseEglConfig();
        if (mEglConfig == null) {
            throw new RuntimeException("eglConfig not initialized");
        }

        mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);

        mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, null);

        if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
            int error = mEgl.eglGetError();
            if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
                Log.e(LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
                return;
            }
            throw new RuntimeException("createWindowSurface failed "
                    + GLUtils.getEGLErrorString(error));
        }

        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
            throw new RuntimeException("eglMakeCurrent failed "
                    + GLUtils.getEGLErrorString(mEgl.eglGetError()));
        }

        mGL = mEglContext.getGL();
    }


    EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
        int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
        return egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);            
    }

    private EGLConfig chooseEglConfig() {
        int[] configsCount = new int[1];
        EGLConfig[] configs = new EGLConfig[1];
        int[] configSpec = getConfig();
        if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
            throw new IllegalArgumentException("eglChooseConfig failed " +
                    GLUtils.getEGLErrorString(mEgl.eglGetError()));
        } else if (configsCount[0] > 0) {
            return configs[0];
        }
        return null;
    }

    private int[] getConfig() {
        return new int[] {
                EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
                EGL10.EGL_RED_SIZE, 8,
                EGL10.EGL_GREEN_SIZE, 8,
                EGL10.EGL_BLUE_SIZE, 8,
                EGL10.EGL_ALPHA_SIZE, 8,
                EGL10.EGL_DEPTH_SIZE, 0,
                EGL10.EGL_STENCIL_SIZE, 0,
                EGL10.EGL_NONE
        };
    }

    void finish() {
        mFinished = true;
    }
}

You could have also went the NDK route and that would likely be a more straightforward transition from Objective C.

like image 108
Pierre-Antoine LaFayette Avatar answered Sep 22 '22 02:09

Pierre-Antoine LaFayette