Как сделать снимок экрана в Windows из приложения Angle OpenGL ES 2.x? - PullRequest
0 голосов
/ 26 сентября 2018

Я использую Angle OpenGL ES 2.x в Windows 7/10 для создания приложения, которое открывает окно для рисования анимации на экране.Из другого приложения (мой регистратор скриншотов) я получаю идентификатор процесса приложения Angle, устанавливаю ловушку Windows, которая использует инъекцию DLL, и заставляет приложение Angle загружать мою пользовательскую библиотеку DLL, которая ожидает команду от моего рекордера скриншотов, чтобы принятьСкриншот.Когда эта команда получена, моя DLL выполняется, и данные пикселей снимка экрана возвращаются в мой регистратор снимков экрана через сообщение Windows.Это все прекрасно работает, когда эмулятор OpenGL ES 2.x PowerVR.Однако при использовании Angle для создания целевого приложения (которое отображает анимацию и которое я хочу сделать на снимке экрана), я получаю только пустые, средне-серые снимки экрана.Вот фрагмент кода DLL, который пытается сделать снимок экрана:

......
......

if (message_info->message == WM_TAKESCREENSHOT)
{
    // We will respond to the window specified in the wParam.
    HWND respond_to = HWND(message_info->wParam);

    RECT client_rect;
    GetClientRect(message_info->hwnd, &client_rect);
    int width = client_rect.right - client_rect.left;
    int height = client_rect.bottom - client_rect.top;

    // 4 bytes for width, 4 bytes for height
    //int padded_row_size = _getPaddedRowSize(width, 3); // Account for the stride -> this works for PowerVR and RGB
    int padded_row_size = _getPaddedRowSize(width, 4); // Account for the stride -> this works for Angle and RGBA

    BYTE* response_message = new BYTE[4 + 4 + padded_row_size * height];
    *reinterpret_cast<int*>(response_message) = width;
    *reinterpret_cast<int*>(response_message + 4) = height;

    BYTE* pixels = response_message + 8;

    // The following statement has been found experimentally, and is needed to
    // take screenshots from back-buffered OpenGL ES apps (like the Windows
    // OpenGL ES target built with the PowerVR libs).

    // This configuration works for PowerVR executables, but not for Angle
    //glReadBuffer(GL_FRONT);

    // This configuration works for PowerVR executables, but not for Angle
    //glReadPixels(0, 0, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels);

    // This configuration works for Angle???
    // This code will be executed only when the executable has been pushed to a deterministic animation state
    // by the application C-code. The executable will remain in that frozen state until the screenshot capture is completed
    // and the bind back to the original framebuffer has completed.

    // For Angle
    GLuint framebufferName;
    GLenum err;
    FILE *fp;
    char errs[30];

    // For Angle
    // Get the name of the currently bound framebuffer

    glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&framebufferName);

    err = glGetError();
    if (GL_NO_ERROR != err)
    {
        fopen_s(&fp, "F:\\errors.log", "a");
        sprintf(errs, "GLGetBind->%d\n", err);
        fprintf(fp, "%s", errs);
        fflush(fp);
        fclose(fp);
    }

    // For Angle
    // Bind to the final render framebuffer - 0 = the screen
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    err = glGetError();
    if (GL_NO_ERROR != err)
    {
        fopen_s(&fp, "F:\\errors.log", "a");
        sprintf(errs, "GLBind->%d\n", err);
        fprintf(fp, "%s", errs);
        fflush(fp);
        fclose(fp);
    }

    // For Angle
    // Read the pixels
    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    // For Angle debugging

    err = glGetError();
    if (GL_NO_ERROR != err)
    {
        fopen_s(&fp, "F:\\errors.log", "a");
        sprintf(errs, "GLrpxl->%d\n", err);
        fprintf(fp, "%s", errs);
        fflush(fp);
        fclose(fp);
    }

    // For Angle
    // Bind back to the original framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);

    err = glGetError();
    if (GL_NO_ERROR != err)
    {
        fopen_s(&fp, "F:\\errors.log", "a");
        sprintf(errs, "GLreBind->%d\n", err);
        fprintf(fp, "%s", errs);
        fflush(fp);
        fclose(fp);
    }


    COPYDATASTRUCT data_to_send;
    data_to_send.dwData = 0;
    data_to_send.cbData = 4 + 4 + padded_row_size * height;
    data_to_send.lpData = LPVOID(response_message);

    SendMessage(respond_to, WM_COPYDATA, WPARAM(message_info->hwnd), reinterpret_cast<LPARAM>(static_cast<LPVOID>(&data_to_send)));
    GetLastError();
    GetCurrentThreadId();
    GetCurrentThreadId();

    delete[] response_message;
}

return ::CallNextHookEx(nullptr, nCode, wParam, lParam);
}

Почти независимо от того, какой вызов OpenGL я делаю, glGetError () возвращает код ошибки 1282, недопустимая операция.На самом деле, в самом начале моей DLL, если я помещу туда glGetError (), я получу ошибку 1282, прежде чем я сделаю что-нибудь в моей DLL.Однако я могу сделать вызов, например, glIsFramebuffer (), чтобы определить, какие кадровые буферы в настоящее время связаны, и этот вызов дает мне правильное возвращаемое значение.Я пытался зацикливать glGetError () до тех пор, пока он не возвращает GL_NO_ERROR, но цикл будет работать вечно.

Я попытался связать кадровый буфер 0 (экран) и выполнить glReadPixels (), чтобы получить скриншот,и я также попробовал кадровый буфер 1, который отображается как единственный связанный кадровый буфер, но результат всегда один и тот же - «пустой» снимок экрана со средне-серыми пикселями.

(Прошу прощения за уродливый код инструментария регистрации ошибок- это помогло в отладке DLL.)

Почему я не могу привязаться к кадровому буферу, который имеет содержимое экрана, и захватить данные пикселей?

...