У чтения из GL_TEXTURE_EXTERNAL_OES до GL_TEXTURE_2D есть проблемы с производительностью и глюки - PullRequest
0 голосов
/ 20 ноября 2018

Мне нужно отправить данные из GL_TEXTURE_EXTERNAL_OES в простой GL_TEXTURE_2D (рендеринг изображения с Android-плеера в текстуру Unity) и в настоящее время делать это через считанные пиксели из буфера с прикрепленной исходной текстурой.Этот процесс работает корректно на моем телефоне OnePlus 5, но у него есть некоторые глюки с изображением на телефонах, таких как xiaomi note 4, mi a2 и т. Д. (Например, изображение очень зеленое), а также есть проблемы с производительностью, потому что этот процесс работает каждый кадр, а затембольше пикселей для чтения, чем хуже производительность (даже у моего телефона низкий fps при разрешении 4k).Любая идея, как оптимизировать этот процесс или как-то иначе?

Спасибо и наилучшими пожеланиями!

GLuint FramebufferName;
glGenFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_EXTERNAL_OES, g_ExtTexturePointer, 0);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
    LOGD("%s", "Error: Could not setup frame buffer.");
}

unsigned char* data = new unsigned char[g_SourceWidth * g_SourceHeight * 4];
glReadPixels(0, 0, g_SourceWidth, g_SourceHeight, GL_RGBA, GL_UNSIGNED_BYTE, data);

glBindTexture(GL_TEXTURE_2D, g_TexturePointer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g_SourceWidth, g_SourceHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

glDeleteFramebuffers(1, &FramebufferName);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);

delete[] data;

ОБНОВЛЕНИЕ. Функция, которая содержит этот код и функциюкоторый вызывает его со стороны Unity

static void UNITY_INTERFACE_API OnRenderEvent(int eventID) { ... }

extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UMDGetRenderEventFunc()
{
    return OnRenderEvent;
}

который вызывается из функции обновления Unity следующим образом:

[DllImport("RenderingPlugin")]
static extern IntPtr UMDGetRenderEventFunc();

IEnumerator UpdateVideoTexture()
{
    while (true)
    {
        ...
        androidPlugin.UpdateSurfaceTexture();
        GL.IssuePluginEvent(UMDGetRenderEventFunc, 1);
    } 
}

И плагин Android делает это на своей стороне (surfaceTexture его текстура, которая содержит эту внешнюю текстуруна котором ExoPlayer рендерит видео)

public void exportUpdateSurfaceTexture() {
    synchronized (this) {
        if (this.mIsStopped) {
            return;
        }
        surfaceTexture.updateTexImage();
    }
}

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

Вызов glReadPixels всегда будет медленным;Процессоры плохо справляются с массовой передачей данных.

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

0 голосов
/ 20 ноября 2018

На стороне C ++:

Вы создаете и уничтожаете данные пикселей каждый кадр, когда вы делаете new unsigned char[g_SourceWidth * g_SourceHeight * 4]; и delete[] data, и это дорого в зависимости от размера текстуры.Создайте данные текстуры один раз, а затем снова используйте их.

Один из способов сделать это состоит в том, чтобы static переменные на стороне C ++ содержали информацию о текстуре, а затем функцию для инициализации этих переменных: *

static void* pixelData = nullptr;
static int _x;
static int _y;
static int _width;
static int _height;

void initPixelData(void* buffer, int x, int y, int width, int height) {
    pixelData = buffer;
    _x = x;
    _y = y;
    _width = width;
    _height = height;
}

Тогда ваша функция захвата должна бытьпереписан для удаления new unsigned char[g_SourceWidth * g_SourceHeight * 4]; и delete[] data, но с использованием статических переменных.

static void UNITY_INTERFACE_API OnRenderEvent(int eventID)
{
    if (pixelData == nullptr) {
        //Debug::Log("Pointer is null", Color::Red);
        return;
    }

    GLuint FramebufferName;
    glGenFramebuffers(1, &FramebufferName);
    glBindFramebuffer(GL_FRAMEBUFFER, FramebufferName);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_EXTERNAL_OES, g_ExtTexturePointer, 0);

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        LOGD("%s", "Error: Could not setup frame buffer.");
    }

    glReadPixels(_x, _y, _width, _height, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);

    glBindTexture(GL_TEXTURE_2D, g_TexturePointer);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);

    glDeleteFramebuffers(1, &FramebufferName);
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
}

extern "C" UnityRenderingEvent UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API
UMDGetRenderEventFunc()
{
    return OnRenderEvent;
}

На стороне C #:

[DllImport("RenderingPlugin", CallingConvention = CallingConvention.Cdecl)]
public static extern void initPixelData(IntPtr buffer, int x, int y, int width, int height);

[DllImport("RenderingPlugin", CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr UMDGetRenderEventFunc();

Создание текстурыинформацию, закрепите его и отправьте указатель на C ++:

int width = 500;
int height = 500;

//Where Pixel data will be saved
byte[] screenData;
//Where handle that pins the Pixel data will stay
GCHandle pinHandler;

//Used to test the color
public RawImage rawImageColor;
private Texture2D texture;

// Use this for initialization
void Awake()
{
    Resolution res = Screen.currentResolution;
    width = res.width;
    height = res.height;

    //Allocate array to be used
    screenData = new byte[width * height * 4];
    texture = new Texture2D(width, height, TextureFormat.RGBA32, false, false);

    //Pin the Array so that it doesn't move around
    pinHandler = GCHandle.Alloc(screenData, GCHandleType.Pinned);

    //Register the screenshot and pass the array that will receive the pixels
    IntPtr arrayPtr = pinHandler.AddrOfPinnedObject();

    initPixelData(arrayPtr, 0, 0, width, height);

    StartCoroutine(UpdateVideoTexture());
}

Затем, чтобы обновить текстуру, см. образец ниже.Обратите внимание, что есть два способа обновить текстуру, как показано в коде ниже.Если у вас возникнут проблемы с Method1, закомментируйте две строки, которые используют texture.LoadRawTextureData и texture.Apply и un-comment код Method2, который использует ByteArrayToColor, texture.SetPixels и texture.Apply функция:

IEnumerator UpdateVideoTexture()
{
    while (true)
    {
        //Take screenshot of the screen
        GL.IssuePluginEvent(UMDGetRenderEventFunc(), 1);

        //Update Texture Method1
        texture.LoadRawTextureData(screenData);
        texture.Apply();

        //Update Texture Method2. Use this if the Method1 above crashes
        /*
        ByteArrayToColor();
        texture.SetPixels(colors);
        texture.Apply();
        */

        //Test it by assigning the texture to a raw image
        rawImageColor.texture = texture;

        //Wait for a frame
        yield return null;
    }
}

Color[] colors = null;

void ByteArrayToColor()
{
    if (colors == null)
    {
        colors = new Color[screenData.Length / 4];
    }

    for (int i = 0; i < screenData.Length; i += 4)
    {
        colors[i / 4] = new Color(screenData[i],
            screenData[i + 1],
            screenData[i + 2],
            screenData[i + 3]);
    }
}

Открепить массив, когда закончите или когда сценарий будет уничтожен:

void OnDisable()
{
    //Unpin the array when disabled
    pinHandler.Free();
}
...