OpenGL纹理ID不止一次生成

时间:2013-03-17 17:10:58

标签: opengl-es android-ndk textures

我在C中有纹理加载例程,它调用我游戏的Java部分来完成这项工作。它可以加载.png以及.jpg文件并从中创建纹理。它返回C生成的textureID(使用glGenTextures生成)。它一直很好用,直到我开始使用前缓冲器。

由于前缓冲的纹理是空的,我在C中直接创建它。当我第一次运行游戏时它工作正常 - 为前缓冲区生成的纹理ID与所有其他纹理ID不同。但是当我按下HOME键,然后再次跳回到游戏中并重新加载所有纹理并再次重新创建前缓冲区(从上下文丢失中恢复)为前缓冲区生成的纹理ID与为纹理生成的纹理ID之一发生冲突 - 如果有的话两条ID线。使用帧缓冲区的屏幕已损坏 - 显示ID等于帧分配器新分配ID的纹理。

这一切都只发生在真实设备上(在我的情况下是三星Galaxy平板电脑),而不是在模拟器中。

C加载例程:

void Texture::construct(AssetLoader* aLoader, s32 aIdx)
{
    const c8* fileName = aLoader->getFileName();

    // load texture
    SBC::System::Application* app = &Game::getGame().getApplication();

    JNIEnv *env;

    bool shouldDetach = false;
    JavaVM* vm = gJavaVM;
    jint rc = vm->GetEnv((void **)&env, JNI_VERSION_1_6);
    if (rc != JNI_OK)
    {
        shouldDetach = true;
        vm->AttachCurrentThread(&env, NULL);
    }
    jclass& activityClass = gJavaActivityClass;

    jmethodID mid = env->GetStaticMethodID(activityClass, "loadTexture", "(Ljava/lang/String;I)I");
    jstring mystr = env->NewStringUTF(fileName);
    jint ret = env->CallStaticIntMethod(activityClass, mid, mystr, aIdx);
    env->DeleteLocalRef(mystr);

    // store information on ID, width and height of texture
    mTextureID = ret;

    LOGE("textureID = %i", mTextureID);

    mid = env->GetStaticMethodID(activityClass, "getTextureWidth", "()I");
    mWidth = env->CallStaticIntMethod(activityClass, mid);
    mid = env->GetStaticMethodID(activityClass, "getTextureHeight", "()I");
    mHeight = env->CallStaticIntMethod(activityClass, mid);

    if (shouldDetach)
        vm->DetachCurrentThread();

    LOGI("texture ID %i, width %i, height %i", mTextureID, mWidth, mHeight);
}

Java方法

//-----------------------------------------------------
/* fname = asset file name
 * id >= 0 ... position in compound file
 * id < 0 ... single file (no compound)
 */
public static int loadTexture(String fname, int id)
{
    // clear last texture parameters
    txtID = width = height = -1;

    Log.d("Helper", "Loading texture from asset file " + fname + " with id " + id);

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inScaled = false;       // No pre-scaling
    AssetManager am = SBCEngine.getSBCEngine().getAssets();
    Bitmap bitmap = null;

    try
    {
        InputStream stream = am.open(fname);

        // loading from compound file?
        if (id >= 0)
        {
            DataInputStream input = new DataInputStream(stream);

            // skip header
            input.skip(3);
            // skip to entry offset
            input.skip(id * 4);
            // read entry beginning
            int dataStart = input.readInt();
            // read data length
            int dataLen = input.readInt() - dataStart;
            // skip to start of subfile
            // offsets are without header (3) bytes
            // we already skipped id * 4 bytes
            // we already have read 2 offset by 4 bytes = 8 in total
            input.skip(dataStart - (id * 4) - 8);

            // get data from correct position
            byte[] data = new byte[dataLen];
            input.read(data);

            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
        }
        else    // no compound
        {
            Log.d("Helper", "Loading from stream");
            bitmap = BitmapFactory.decodeStream(stream, null, options);
        }


        // test returned bitmap for success
        if (bitmap == null)
        {
            Log.e("Helper", "Failed to load texture " + fname + " with id " + id);
        }

        // check whether the loaded bitmap has width and height equal to power of 2
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        if (getNearestPOT(w) != w || getNearestPOT(h) != h)
        {
            Log.w("Helper", "Texture " + fname + " with id " + id +
                    " has not either width or height power of 2");

            // new dimensions
            w = getNearestPOT(w);
            h = getNearestPOT(h);

            // get actual bitmap config
            Bitmap.Config bitmapConfig = bitmap.getConfig();
            // check for null
            if (bitmapConfig == null)
            {
                bitmapConfig = Bitmap.Config.ARGB_8888;
                Log.w("Helper", "Unknown bitmap config. Setting to ARGB_8888");
            }
            // redraw bitmap into POT bitmap
            Bitmap newBitmap = Bitmap.createBitmap(w, h, bitmapConfig);
            Canvas canvas = new Canvas(newBitmap);
            canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
            bitmap.recycle();
            canvas = null;
            bitmap = newBitmap;

            Log.w("Helper", "Texture " + fname + " rebuilded into texture with POT");
        }

        // generate textureID
        int[] textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);
        int textureID = textures[0];

        // create texture
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

        // destroy bitmap
        bitmap.recycle();

        txtID = textureID;
        width = w;
        height = h;

        Log.d("Helper", "Loaded texture ID:" + textureID + ", width:" + w + ", height:" + h);
        return textureID;
    }
    catch (IOException e)
    {
        Log.e("Helper", "Failed to load texture " + fname + " with id " + id);
        return 0;
    }
}

//------------------------------------------------------------------------
public static int getTextureWidth()
{
    return width;
}

//------------------------------------------------------------------------
public static int getTextureHeight()
{
    return height;
}

//------------------------------------------------------------------------
private static int getNearestPOT(int val)
{
    int newDim = 1;
    while(val > newDim) newDim *= 2;
    return newDim;
}

最后是用于为前缓冲区创建纹理的C例程

// create empty texture
void Texture::construct(u32 aTextureWidth, u32 aTextureHeight)
{
    // get power of 2 dimensions
    mWidth = getNearestPOT(aTextureWidth);
    mHeight = getNearestPOT(aTextureHeight);

    glGenTextures(1, &mTextureID);
    glBindTexture(GL_TEXTURE_2D, mTextureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mWidth, mHeight, 0,
            GL_RGB, GL_UNSIGNED_SHORT_5_6_5, null);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    LOGE("textureID = %i", mTextureID);
}

有人遇到同样的问题吗?

1 个答案:

答案 0 :(得分:2)

我终于找到了解决方案。当发生上下文丢失时,将删除所有纹理。但是在重新创建以前使用过的ID之前,我自己删除了它们。在此过程中导致混乱,因为在更新下一个对象时会立即删除一些新生成的ID。