Почему текстуры POT работают медленнее, чем без pot?

#android #textures #opengl-es-2.0

#Android #Текстуры #opengl-es-2.0

Вопрос:

Я использую следующую функцию для загрузки текстур

 public static int loadTexture(Bitmap bmp)
{
    final int[] textureHandle = new int[1];

    GLES20.glGenTextures(1, textureHandle, 0);
    if (textureHandle[0] != 0)
    {

        // Bind to the texture in OpenGL
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);

        // Set filtering
        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.glGenerateMipmap(textureHandle[0]);
        //adapt texture to POT
        int adaptedWidth= (int) Math.pow(2,Math.ceil(Math.log(bmp.getWidth())/Math.log(2d)));
        int adaptedHeight= (int) Math.pow(2,Math.ceil(Math.log(bmp.getHeight())/Math.log(2d)));
        Log.d("texture",adaptedWidth "," adaptedHeight);

        Bitmap tmp = Bitmap.createScaledBitmap(bmp, adaptedWidth, adaptedHeight, false);
        Log.d("asize",tmp.getWidth() "," tmp.getHeight());
        // Load the bitmap into the bound texture.
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, tmp, 0);
        //GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
        tmp.recycle();
        // Recycle the bitmap, since its data has been loaded into OpenGL.
        //bmp.recycle();
    }

    if (textureHandle[0] == 0)
    {
        throw new RuntimeException("Error loading texture.");
    }
    return textureHandle[0];
}
  

С помощью этого кода я получил 14-17 кадров в секунду.Независимо от того, загружаю ли я свое растровое изображение (которое не является POT) напрямую, без адаптации к POT.Частота кадров в секунду подскакивает до 28-30.Я думал, что текстуры банка должны работать быстрее, чем без банка.Есть ли этому объяснение?

UPD: код рендеринга:

 @Override
public void onDrawFrame(GL10 gl) {
    //curScale=modelMatrix[SCALE_X];
    TimeMeasurer.reset();
    long curTS= SystemClock.uptimeMillis();
    long frameRenderTime=curTS-ts;
    //Log.d("renderer","FPS:" 1000.0/frameRenderTime);
    Log.d("renderer","frame render time:" frameRenderTime);
    ts=curTS;
    GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
    if (piecesMesh!=null) {
        Matrix.setIdentityM(MVPMatrix,0);
        Matrix.multiplyMM(MVPMatrix,0,projMatrix,0,modelMatrix,0);
        drawPassivePieces();
        drawActivePieces();
        if (helper!=null) {
            drawHelper();
        }
    }
    TimeMeasurer.measure("onDrawFrame execution time:");
}
private void drawPassivePieces() {
    //shadows
    shadowProgram.useProgram();
    shadowProgram.setUniforms(MVPMatrix,textureMaskId);
    shadowMesh.bindPieceData(shadowProgram,false);
    shadowMesh.drawPieces(false);
    shadowMesh.disableAttributes(shadowProgram);
    //pieces
    piecesProgram.useProgram();
    piecesProgram.setUniforms(MVPMatrix, textureImageId, textureMaskId);
    piecesMesh.bindPieceData(piecesProgram,false);
    piecesMesh.drawPieces(false);
    piecesMesh.disableAttributes(piecesProgram);

}
private void drawActivePieces() {
    //shadows
    shadowProgram.useProgram();
    shadowProgram.setUniforms(MVPMatrix,textureMaskId);
    shadowMesh.bindPieceData(shadowProgram,true);
    shadowMesh.drawPieces(true);
    shadowMesh.disableAttributes(shadowProgram);
    //pieces
    piecesProgram.useProgram();
    piecesProgram.setUniforms(MVPMatrix, textureImageId, textureMaskId);
    piecesMesh.bindPieceData(piecesProgram,true);
    piecesMesh.drawPieces(true);
    piecesMesh.disableAttributes(piecesProgram);
}
public void drawHelper() {
    helperProgram.useProgram();
    helper.bindData(helperProgram);
    helper.draw();
    helper.disableAttributes(helperProgram);
}
  

Ответ №1:

Без подробного анализа производительности на самом деле невозможно сделать больше, чем строить предположения.

Одна из вероятных причин заключается в том, что ваш рендеринг ограничен пропускной способностью памяти для выборки текстур. Если вы увеличите текстуру, общий объем доступной памяти увеличится, что приведет к замедлению.

Или, что очень связано с вышесказанным, частота попадания в кэш для выборки текстур падает, если выбранные тексели расположены дальше в памяти, что происходит при увеличении масштаба текстуры. Более низкий уровень попадания в кэш означает более низкую производительность.

Вам действительно не следует искусственно увеличивать текстуру больше, чем необходимо, если только это не требуется из-за ограниченной поддержки NPOT в ES 2.0, а используемое вами оборудование не рекламирует расширение OES_texture_npot. Я сомневаюсь, что кто-либо долгое время создавал оборудование, предпочитающее горшечные текстуры.

Комментарии:

1. Добавлен код рендеринга. Также я провел некоторые измерения: NPOT: время рендеринга кадра: 39 Время выполнения onDraw: 8 Время рендеринга кадра: 73 Время выполнения onDraw: 8 Итак, проблема во времени рисования, которое, как я предполагаю, является разницей между frame rendering time и onDraw execution time

2. Также я изменил код загрузки текстур, чтобы он не увеличивал растровое изображение, а просто рисовал мою текстуру в новом большем пустом бимапе — тот же результат.

3. @undefined Все эти наблюдения согласуются с моей теорией.

4. Согласно вашей теории, я ничего не могу сделать, чтобы исправить это. Я прав?

5. Я не знаю, что произошло, но мой fps вернулся к 30 . Я внес некоторые коррективы, чтобы мои координаты текстуры не выходили за пределы фактического размера изображения. Я не знаю почему, но это сильно влияет на время рендеринга Спасибо всем!

Ответ №2:

В использовании потовых текстур есть большие преимущества. В OpenGLES 2.0 они позволяют использовать mipmaps и полезные режимы адресации текстур, такие как repeat. Вы также можете использовать память более эффективно, потому что многие реализации выделяют память так, как будто ваша текстура в любом случае является POT.

Однако в этом случае, когда вы просто берете не-горшечную текстуру и масштабируете ее, я бы ожидал, что в результате производительность будет немного хуже. Вы упускаете большой потенциальный выигрыш, потому что не используете mipmaps. Используя текстуру большего размера, вы просто запрашиваете больше текстурного кэша графического процессора, потому что полезные части изображения теперь более распределены в памяти, чем раньше.

Другой способ взглянуть на это заключается в том, что в отсутствие mipmapping большие текстуры будут работать хуже, чем маленькие текстуры, а ваш процесс масштабирования просто увеличивает вашу текстуру.

Я удивлен, что разница настолько заметна — вы уверены, что ваш кодовый путь масштабирования не вызывает ничего неожиданного, например, слишком большого изменения размера или выбора другого формата текстуры или режима фильтрации?

Комментарии:

1. Я использую свое растровое изображение в качестве источника в createScaledBitmap, поэтому формат пикселей должен быть таким же. Фильтры не применяются. Размер моей текстуры 1792×1536; адаптированный размер: 2048×2048 Я рисую 2220 полигонов одним фрагментным шейдером (используя две текстуры), а затем 2220 полигонов другим фрагментным шейдером. Для меня тоже удивительно, почему один и тот же код так сильно зависит от размера текстуры.