Окраска нескольких окон - PullRequest
1 голос
/ 23 апреля 2019

Как я могу избежать того, что моя частота кадров застревает в окне узкого места?

  • У меня есть одно окно, которое работает со скоростью 10 кадров в секунду, когда отображается один.
  • У меня есть другое окнос частотой 60 кадров в секунду при одном отображении.

Моя проблема в том, что я получаю 10 кадров в секунду при отображении двух окон вместе.

Я ожидал, что быстрое окно будетбыстрее, чем медленное окно, но оба окна получают одинаковую медленную скорость FPS при рисовании одновременно.

Я ожидал получить около 5 кадров в секунду для медленного и 30 кадров в секунду для быстрого окна.

Попробуйте использовать функцию timeSetEvent () вместо SetTimer (), и оно будет рисовать только медленное окно, если оно установлено с крошечным временем.

Попробуйте пользовательское сообщение рисования с помощью timeSetEvent (TIME_ONESHOT), которое даету меня такой же медленный результат, как и у WM_TIMER.

Добавлены кнопки включения / выключения, чтобы легко видеть, что делает FPS без изменения кода.

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

Здесь полный код main.c

#include <stdio.h>
#include <string.h>
#include <windows.h>

#define TIMER_WND0      0
#define TIMER_WND1      1

#define OFF_ON_WND0     0
#define OFF_ON_WND1     1

LRESULT CALLBACK procWndMain(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK procWnd0(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK procWnd1(HWND, UINT, WPARAM, LPARAM);

HINSTANCE hInst = NULL;
HWND hWndMain = NULL;
HWND hWnd0 = NULL;
HWND hWnd1 = NULL;

int offOnWnd0 = 1;
int offOnWnd1 = 1;

void uint2str(char *buf, unsigned int x) {
    sprintf(buf, "%u", x);
    }
double millitimeTick(void) {
    return GetTickCount()/1000.0;
    }
int randInt(int a, int b) {
    return rand()%(b-a) +a;
    }

void createWindowClass(HINSTANCE hInstance, WNDPROC wndProc, LPCSTR lpszClassName, int bg, unsigned int style) {
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = style;
    wc.lpfnWndProc = wndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = lpszClassName;
    if(bg) { wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); }
    else{ wc.hbrBackground = NULL; }
    if(!RegisterClassEx(&wc)) {
        MessageBox(NULL, lpszClassName, "Error RegisterClassEx", MB_OK);
        }
    }

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszArg, int nCmdShow) {
    (void)hPrevInst;
    (void)lpszArg;
    MSG message = {0};
    hInst = hInstance;

    createWindowClass(hInstance, procWndMain, "procWndMain", 1, CS_HREDRAW|CS_VREDRAW);
    hWndMain = CreateWindowEx(
        WS_EX_CONTROLPARENT,
        "procWndMain",
        "multiWndPaint",
        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
        (int)CW_USEDEFAULT, (int)CW_USEDEFAULT,
        950, 370,
        HWND_DESKTOP,
        NULL,
        hInstance,
        NULL
        );
    ShowWindow(hWndMain, nCmdShow);
    UpdateWindow(hWndMain);

    while(GetMessage(&message, NULL, 0, 0)) {
        TranslateMessage(&message);
        DispatchMessage(&message);
        }

    UnregisterClass("procWndMain", hInst);
    return (int)message.wParam;
    }

LRESULT CALLBACK procWndMain(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch(msg) {
        case WM_CREATE: {
            createWindowClass(hInst, procWnd0, "procWnd0", 1, 0);
            hWnd0 = CreateWindowEx(
                0,
                "procWnd0",
                "",
                WS_CHILD | WS_VISIBLE | WS_DLGFRAME,
                20, 22,
                440, 300,
                hwnd,
                NULL,
                NULL,
                NULL
                );
            ShowWindow(hWnd0, SW_SHOW);

            createWindowClass(hInst, procWnd1, "procWnd1", 1, 0);
            hWnd1 = CreateWindowEx(
                0,
                "procWnd1",
                "",
                WS_CHILD | WS_VISIBLE | WS_DLGFRAME,
                480, 22,
                440, 300,
                hwnd,
                NULL,
                NULL,
                NULL
                );
            ShowWindow(hWnd1, SW_SHOW);

            HWND btnWnd0 = CreateWindowEx(0, "BUTTON", "Off/On Wnd0",
                WS_CHILD | WS_VISIBLE,
                20, 0, 104, 24,
                hwnd,
                (HMENU)OFF_ON_WND0,
                NULL, NULL
                );
            ShowWindow(btnWnd0, SW_SHOW);

            HWND btnWnd1 = CreateWindowEx(0, "BUTTON", "Off/On Wnd1",
                WS_CHILD | WS_VISIBLE,
                480, 0, 104, 24,
                hwnd,
                (HMENU)OFF_ON_WND1,
                NULL, NULL
                );
            ShowWindow(btnWnd1, SW_SHOW);
            break;
            }

        case WM_COMMAND: {
            if(LOWORD(wParam) == OFF_ON_WND0) {
                if(offOnWnd0) {
                    KillTimer(hWnd0, TIMER_WND0);
                    offOnWnd0 = 0;
                    }
                else{
                    SetTimer(hWnd0, TIMER_WND0, 1, NULL);
                    offOnWnd0 = 1;
                    }
                }

            else if(LOWORD(wParam) == OFF_ON_WND1) {
                if(offOnWnd1) {
                    KillTimer(hWnd1, TIMER_WND1);
                    offOnWnd1 = 0;
                    }
                else{
                    SetTimer(hWnd1, TIMER_WND1, 1, NULL);
                    offOnWnd1 = 1;
                    }
                }
            break;
            }

        case WM_DESTROY: {
            KillTimer(hWnd0, TIMER_WND0);
            KillTimer(hWnd1, TIMER_WND1);
            UnregisterClass("procWnd0", hInst);
            UnregisterClass("procWnd1", hInst);
            PostQuitMessage(0);
            break;
            }

        default: {
            return DefWindowProc(hwnd, msg, wParam, lParam);
            }
        }
    return 0;
    }

void wndInfo(HDC *hdcMem, char *name, unsigned int frame, unsigned int frameRate, unsigned int frameMax) {
    char buf64[24];
    char textInfo[256];
    RECT rect = {0};

    strcpy(textInfo, name);
    strcat(textInfo, "\n");

    uint2str(buf64, frame);
    strcat(textInfo, "Frame : ");
    strcat(textInfo, buf64);

    uint2str(buf64, frameRate);
    strcat(textInfo, "\nFPS : ");
    strcat(textInfo, buf64);

    uint2str(buf64, frameMax);
    strcat(textInfo, "\nMax : ");
    strcat(textInfo, buf64);

    HBRUSH brush = (HBRUSH)GetStockObject(WHITE_BRUSH);
    rect.right = 100;
    rect.bottom = 70;
    FillRect(*hdcMem, &rect, brush);

    rect.left = 4;
    rect.top = 2;
    rect.right = 100;
    rect.bottom = 70;
    SetTextColor(*hdcMem, RGB(0,0,255));
    DrawText(*hdcMem, textInfo, -1, &rect, DT_NOCLIP|DT_NOPREFIX);

    DeleteObject(brush);
    }

LRESULT CALLBACK procWnd0(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static double timestamp = 0;
    static unsigned int frame = 0;
    static unsigned int frameRate = 0;
    static unsigned int frameMax = 0;

    switch(msg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            RECT rect = {0};
            GetClientRect(hwnd, &rect);

            HDC hdcMem = CreateCompatibleDC(hdc);
            HBITMAP bmpMem = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
            HBITMAP bmpMemOld = SelectObject(hdcMem, bmpMem);

            SetBkMode(hdcMem, TRANSPARENT);
            SetTextColor(hdcMem, RGB(255,255,255));

            HBRUSH brush = (HBRUSH)GetStockObject(GRAY_BRUSH);
            FillRect(hdcMem, &rect, brush);

            POINT point = {0};
            size_t i = 0;
            size_t len = 99999;
            while(i < len) {
                point.x = randInt(-rect.right*8, rect.right*8);
                point.y = randInt(-rect.bottom*8, rect.bottom*8);
                TextOut(hdcMem, point.x, point.y, "foobar", 6);
                i++;
                }

            frame++;
            double timestampCur = millitimeTick();
            if(timestampCur > timestamp+1.0) {
                timestamp = timestampCur;
                frameRate = frame;
                if(frame > frameMax) { frameMax = frame; }
                frame = 0;
                }

            wndInfo(&hdcMem, "Wnd0", frame, frameRate, frameMax);

            BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);

            DeleteObject(brush);
            SelectObject(hdcMem, bmpMemOld);
            DeleteDC(hdcMem);
            DeleteObject(bmpMem);
            EndPaint(hwnd, &ps);
            break;
            }

        case WM_ERASEBKGND: {
            break;
            }

        case WM_TIMER: {
            if(wParam == TIMER_WND0) {
                InvalidateRect(hwnd, NULL, TRUE);
                }
            break;
            }

        case WM_CREATE: {
            SetTimer(hwnd, TIMER_WND0, 1, NULL);
            break;
            }

        default: {
            return DefWindowProc(hwnd, msg, wParam, lParam);
            }
        }
    return 0;
    }

LRESULT CALLBACK procWnd1(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    static double timestamp = 0;
    static unsigned int frame = 0;
    static unsigned int frameRate = 0;
    static unsigned int frameMax = 0;

    switch(msg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);
            RECT rect = {0};
            GetClientRect(hwnd, &rect);

            HDC hdcMem = CreateCompatibleDC(hdc);
            HBITMAP bmpMem = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);
            HBITMAP bmpMemOld = SelectObject(hdcMem, bmpMem);

            SetBkMode(hdcMem, TRANSPARENT);
            SetTextColor(hdcMem, RGB(255,255,255));

            HBRUSH brush = (HBRUSH)GetStockObject(GRAY_BRUSH);
            FillRect(hdcMem, &rect, brush);

            POINT point = {0};
            size_t i = 0;
            size_t len = 999;
            while(i < len) {
                point.x = randInt(-rect.right*8, rect.right*8);
                point.y = randInt(-rect.bottom*8, rect.bottom*8);
                TextOut(hdcMem, point.x, point.y, "foobar", 6);
                i++;
                }

            frame++;
            double timestampCur = millitimeTick();
            if(timestampCur > timestamp+1.0) {
                timestamp = timestampCur;
                frameRate = frame;
                if(frame > frameMax) { frameMax = frame; }
                frame = 0;
                }

            wndInfo(&hdcMem, "Wnd1", frame, frameRate, frameMax);

            BitBlt(hdc, 0, 0, rect.right, rect.bottom, hdcMem, 0, 0, SRCCOPY);

            DeleteObject(brush);
            SelectObject(hdcMem, bmpMemOld);
            DeleteDC(hdcMem);
            DeleteObject(bmpMem);
            EndPaint(hwnd, &ps);
            break;
            }

        case WM_ERASEBKGND: {
            break;
            }

        case WM_TIMER: {
            if(wParam == TIMER_WND1) {
                InvalidateRect(hwnd, NULL, TRUE);
                }
            break;
            }

        case WM_CREATE: {
            SetTimer(hwnd, TIMER_WND1, 1, NULL);
            break;
            }

        default: {
            return DefWindowProc(hwnd, msg, wParam, lParam);
            }
        }
    return 0;
    }

1 Ответ

2 голосов
/ 24 апреля 2019

Ваше окно 60 FPS занимает приблизительно 16 мс или меньше для рисования. Я говорю « или меньше », потому что, хотя вы запрашивали интервал таймера 1 мс, вы, вероятно, получаете что-то порядка 16 мс, поскольку это разрешение по умолчанию для этих таймеров. Таким образом, ваше быстрое окно ограничено разрешением таймера, а не скоростью рисования.

Ваше окно 10 FPS занимает примерно 100 мс, и «следующий» интервал уже прошел, поэтому таймер должен сработать сразу. Тот факт, что для следующего кадра требуется около 100 мс, говорит о том, что фактическое время рисования очень близко к 100 мс.

Когда вы показываете оба окна одновременно, они не нарисованы параллельно. Сообщения WM_TIMER отправляются, когда ваш цикл сообщений вызывает GetMessage и DispatchMessage. Если ваш код занят окном 1, когда истекает таймер окна 2, то сообщение WM_TIMER окна 2 будет задерживаться до тех пор, пока окно 1 не будет завершено, и не вернется из своей оконной процедуры в цикл сообщений.

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

(Есть и другие осложнения, но они являются доминирующими факторами в вашем эксперименте.)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...