В C ++ Win32, как я могу предотвратить мерцание окна? - PullRequest
0 голосов
/ 29 марта 2020

Для собственного удовольствия я создаю программу трехмерной графики на C ++ с Win32, которая не использует чисел с плавающей запятой. По какой-то причине окно иногда мерцает при обновлении окна. Я полагаю, что это вызвано обновлением окна после того, как было удалено то, что было ранее показано в окне, и до того, как будет нарисовано следующее. Я нашел несколько веб-страниц и вопросов с ответами о том, что кажется той же самой проблемой, за исключением того, что решения не работают.

Некоторые примеры {

Не используйте CS_HREDRAW | CS_VREDRAW. (Нет заметного эффекта)

Возврат 1 для сообщения окна 'WM_ERASEBKGND'. (Мне нужно стереть то, что было нарисовано ранее. Это останавливает мерцание, предотвращая его стирание.)

Вместо стирания нарисуйте прямоугольник в случае WM_PAINT. (Это еще хуже)

}

Как на самом деле предотвратить мерцание окна?

// compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c
#include <windows.h>
#include <windowsx.h>
#include <tchar.h>
boolean Draw;
//Trigonometry tables in increments of 1/256 of a rotation and multiplied by 65536
const int Total=3,Sine[256]={0,1608,3216,4821,6424,8022,9616,11204,12785,14359,15924,17479,19024,20557,22078,23586,25080,26558,28020,29466,30893,32303,33692,35062,36410,37736,39040,40320,41576,42806,44011,45190,46341,47464,48559,49624,50660,51665,52639,53581,54491,55368,56212,57022,57798,58538,59244,59914,60547,61145,61705,62228,62714,63162,63572,63944,64277,64571,64827,65043,65220,65358,65457,65516,65536,65516,65457,65358,65220,65043,64827,64571,64277,63944,63572,63132,62714,62228,61705,61145,60547,59914,59244,58538,57798,57022,56212,55368,54491,53581,52639,51665,50660,49624,48559,47464,46341,45190,44011,42806,41576,40320,39040,37736,36410,35062,33692,32303,30893,29466,28020,26558,25080,23586,22078,20557,19024,17479,15924,14359,12785,11204,9616,8022,6424,4821,3216,1608,0,-1608,-3216,-4821,-6424,-8022,-9616,-11204,-12785,-14359,-15924,-17479,-19024,-20557,-22078,-23586,-25080,-26558,-28020,-29466,-30893,-32303,-33692,-35062,-36410,-37736,-39040,-40320,-41576,-42806,-44011,-45190,-46341,-47464,-48559,-49624,-50660,-51665,-52639,-53581,-54491,-55368,-56212,-57022,-57798,-58538,-59244,-59914,-60547,-61145,-61705,-62228,-62714,-63162,-63572,-63944,-64277,-64571,-64827,-65043,-65220,-65358,-65457,-65516,-65536,-65516,-65457,-65358,-65220,-65043,-64827,-64571,-64277,-63944,-63572,-63162,-62714,-62228,-61705,-61145,-60547,-59914,-59244,-58538,-57798,-57022,-56212,-55368,-54491,-53581,-52639,-51665,-50660,-49624,-48559,-47464,-46341,-45190,-44011,-42806,-41576,-40320,-39040,-37736,-36410,-35062,-33692,-32303,-30893,-29466,-28020,-26558,-25080,-23586,-22078,-20557,-19024,-17479,-15924,-14359,-12785,-11204,-9616,-8022,-6424,-4821,-3216,-1608},Cosine[256]={65536,65516,65457,65358,65220,65043,64827,64571,64277,63944,63572,63132,62714,62228,61705,61145,60547,59914,59244,58538,57798,57022,56212,55368,54491,53581,52639,51665,50660,49624,48559,47464,46341,45190,44011,42806,41576,40320,39040,37736,36410,35062,33692,32303,30893,29466,28020,26558,25080,23586,22078,20557,19024,17479,15924,14359,12785,11204,9616,8022,6424,4821,3216,1608,0,-1608,-3216,-4821,-6424,-8022,-9616,-11204,-12785,-14359,-15924,-17479,-19024,-20557,-22078,-23586,-25080,-26558,-28020,-29466,-30893,-32303,-33692,-35062,-36410,-37736,-39040,-40320,-41576,-42806,-44011,-45190,-46341,-47464,-48559,-49624,-50660,-51665,-52639,-53581,-54491,-55368,-56212,-57022,-57798,-58538,-59244,-59914,-60547,-61145,-61705,-62228,-62714,-63162,-63572,-63944,-64277,-64571,-64827,-65043,-65220,-65358,-65457,-65516,-65536,-65516,-65457,-65358,-65220,-65043,-64827,-64571,-64277,-63944,-63572,-63162,-62714,-62228,-61705,-61145,-60547,-59914,-59244,-58538,-57798,-57022,-56212,-55368,-54491,-53581,-52639,-51665,-50660,-49624,-48559,-47464,-46341,-45190,-44011,-42806,-41576,-40320,-39040,-37736,-36410,-35062,-33692,-32303,-30893,-29466,-28020,-26558,-25080,-23586,-22078,-20557,-19024,-17479,-15924,-14359,-12785,-11204,-9616,-8022,-6424,-4821,-3216,-1608,0,1608,3216,4821,6424,8022,9616,11204,12785,14359,15924,17479,19024,20557,22078,23586,25080,26558,28020,29466,30893,32303,33692,35062,36410,37736,39040,40320,41576,42806,44011,45190,46341,47464,48559,49624,50660,51665,52639,53581,54491,55368,56212,57022,57798,58538,59244,59914,60547,61145,61705,62228,62714,63162,63572,63944,64277,64571,64827,65043,65220,65358,65457,65516};
int HorizontalAxis,VerticalAxis,SemiHorizontalAxis,SemiVerticalAxis,Speed=1,Phi=64,Theta,VelX,VelY,VelZ,SinTheta,CosTheta=65536,SinPhi,CosPhi=-65536,RegX,RegY,RegZ,Quadrilaterals[3][6]={{0,1,2,3,1,0},{0,3,4,5,0,1},{6,7,8,9,1,2}};
LONG64 AxAv,X,Y,Z,XCoordinates[10]={-5,-5,5,5,5,-5,60,60,120,120},YCoordinates[10]={-5,5,5,-5,-5,-5,80,140,140,80},ZCoordinates[10]={10,10,10,10,30,30,100,100,200,200},q$,w$,e$,r$,t$,y$,u$,i$;
POINT Points[3][4]={{{0,0},{0,0},{0,0},{0,0}}};
RECT WindowRect;
const HPEN Pens[2]={CreatePen(PS_NULL,0,0),CreatePen(PS_SOLID,1,RGB(0,0,0))};
const HBRUSH Brushes[3]={CreateSolidBrush(RGB(0,0,0)),CreateSolidBrush(RGB(128,128,128)),CreateSolidBrush(RGB(255,255,255))};
HINSTANCE hInst;
HWND hWnd;
PAINTSTRUCT ps;
HDC hdc;
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam){
    switch(message){
    case WM_KEYDOWN:
        if(GetAsyncKeyState(16)){Speed=1+3*Speed/2;}
        if(GetAsyncKeyState(17)){
            Speed=2*Speed/3-1;
            if(Speed<1){Speed=1;}
        }if(GetAsyncKeyState(82)){
            X=0;Y=0;Z=0;Theta=0;Phi=0;RegX=0;RegY=0;RegZ=0;
            SinTheta=0;CosTheta=65536;SinPhi=0;CosPhi=65536;
        }return 0;
    case WM_SIZE:
        GetWindowRect(hWnd,&WindowRect);
        HorizontalAxis=WindowRect.right-WindowRect.left;
        SemiHorizontalAxis=HorizontalAxis/2;
        VerticalAxis=WindowRect.bottom-WindowRect.top-100;
        SemiVerticalAxis=VerticalAxis/2;
        AxAv=(HorizontalAxis+VerticalAxis)/2;
        return 0;
    case WM_PAINT:
        hdc=BeginPaint(hWnd,&ps);
        if(GetAsyncKeyState(83)){
            if(Phi>0){
                Phi--;
                SinPhi=Cosine[Phi];CosPhi=-Sine[Phi];
            }
        }if(GetAsyncKeyState(87)){
            if(Phi<128){
                Phi++;
                SinPhi=Cosine[Phi];CosPhi=-Sine[Phi];
            }
        }if(GetAsyncKeyState(65)){
            Theta--;
            if(Theta<0){Theta=255;}
            SinTheta=Sine[Theta];CosTheta=Cosine[Theta];
        }if(GetAsyncKeyState(68)){
            Theta++;
            if(Theta>255){Theta=0;}
            SinTheta=Sine[Theta];CosTheta=Cosine[Theta];
        }if(GetAsyncKeyState(39)){VelZ=-SinTheta*Speed;VelX=-CosTheta*Speed;}
        if(GetAsyncKeyState(38)){VelZ+=CosTheta*Speed;VelX-=SinTheta*Speed;}
        if(GetAsyncKeyState(37)){VelZ+=SinTheta*Speed;VelX+=CosTheta*Speed;}
        if(GetAsyncKeyState(40)){VelZ-=CosTheta*Speed;VelX+=SinTheta*Speed;}
        if(GetAsyncKeyState(81)){VelY=65536*Speed;}
        if(GetAsyncKeyState(69)){VelY=-65536*Speed;}
        RegX+=VelX;RegY+=VelY;RegZ+=VelZ;
        X+=RegX/65536;Y+=RegY/65536;Z+=RegZ/65536;
        RegX%=65536;RegY%=65536;RegZ%=65536;
        VelX=0;VelY=0;VelZ=0;
        q$=0;
        while(q$<Total){
            Draw=false;
            w$=Quadrilaterals[q$][0];
            e$=65536*(XCoordinates[w$]-X);
            r$=65536*(YCoordinates[w$]-Y);
            t$=65536*(ZCoordinates[w$]-Z);
            y$=(CosTheta*e$+SinTheta*t$)/65536;
            e$=(CosTheta*t$-SinTheta*e$)/65536;
            u$=(CosPhi*r$+SinPhi*e$)/65536;
            i$=(CosPhi*e$-SinPhi*r$)/65536;
            if(i$<0){
                Draw=true;
                Points[q$][0].x=SemiHorizontalAxis+AxAv*y$/i$;
                Points[q$][0].y=SemiVerticalAxis+AxAv*u$/i$;
            }w$=Quadrilaterals[q$][1];
            e$=65536*(XCoordinates[w$]-X);
            r$=65536*(YCoordinates[w$]-Y);
            t$=65536*(ZCoordinates[w$]-Z);
            y$=(CosTheta*e$+SinTheta*t$)/65536;
            e$=(CosTheta*t$-SinTheta*e$)/65536;
            u$=(CosPhi*r$+SinPhi*e$)/65536;
            i$=(CosPhi*e$-SinPhi*r$)/65536;
            if(i$<0){
                Draw=true;
                Points[q$][1].x=SemiHorizontalAxis+AxAv*y$/i$;
                Points[q$][1].y=SemiVerticalAxis+AxAv*u$/i$;
            }w$=Quadrilaterals[q$][2];
            e$=65536*(XCoordinates[w$]-X);
            r$=65536*(YCoordinates[w$]-Y);
            t$=65536*(ZCoordinates[w$]-Z);
            y$=(CosTheta*e$+SinTheta*t$)/65536;
            e$=(CosTheta*t$-SinTheta*e$)/65536;
            u$=(CosPhi*r$+SinPhi*e$)/65536;
            i$=(CosPhi*e$-SinPhi*r$)/65536;
            if(i$<0){
                Draw=true;
                Points[q$][2].x=SemiHorizontalAxis+AxAv*y$/i$;
                Points[q$][2].y=SemiVerticalAxis+AxAv*u$/i$;
            }w$=Quadrilaterals[q$][3];
            e$=65536*(XCoordinates[w$]-X);
            r$=65536*(YCoordinates[w$]-Y);
            t$=65536*(ZCoordinates[w$]-Z);
            y$=(CosTheta*e$+SinTheta*t$)/65536;
            e$=(CosTheta*t$-SinTheta*e$)/65536;
            u$=(CosPhi*r$+SinPhi*e$)/65536;
            i$=(CosPhi*e$-SinPhi*r$)/65536;
            if(i$<0){
                Draw=true;
                Points[q$][3].x=SemiHorizontalAxis+AxAv*y$/i$;
                Points[q$][3].y=SemiVerticalAxis+AxAv*u$/i$;
            }if(Draw){
                SelectPen(hdc,Pens[Quadrilaterals[q$][4]]);
                SelectBrush(hdc,Brushes[Quadrilaterals[q$][5]]);
                Polygon(hdc,Points[q$],4);
            }q$++;
        }EndPaint(hWnd,&ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:return DefWindowProc(hWnd,message,wParam,lParam);
    }
}
int CALLBACK WinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInstance,_In_ LPSTR lpCmdLine,_In_ int nCmdShow){
    GetWindowRect(GetDesktopWindow(),&WindowRect);
    HorizontalAxis=WindowRect.right;
    SemiHorizontalAxis=HorizontalAxis/2;
    VerticalAxis=WindowRect.bottom-100;
    SemiVerticalAxis=VerticalAxis/2;
    AxAv=(HorizontalAxis+VerticalAxis)/2;
    WNDCLASSEX wcex;
    wcex.cbSize=sizeof(WNDCLASSEX);
    wcex.style=CS_HREDRAW|CS_VREDRAW;
    wcex.lpfnWndProc=WndProc;
    wcex.cbClsExtra=0;
    wcex.cbWndExtra=0;
    wcex.hInstance=hInstance;
    wcex.hIcon=LoadIcon(hInstance,IDI_APPLICATION);
    wcex.hCursor=LoadCursor(NULL,IDC_ARROW);
    wcex.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName=NULL;
    wcex.lpszClassName=_T("DesktopApp");
    wcex.hIconSm=LoadIcon(wcex.hInstance,IDI_APPLICATION);
    if(!RegisterClassEx(&wcex)){
        MessageBox(NULL,_T("RegisterClassEx failed!"),_T("Something"),NULL);
        return 1;
    }hInst=hInstance;
    hWnd=CreateWindow(_T("DesktopApp"),_T("Something Application"),WS_OVERLAPPEDWINDOW,0,0,HorizontalAxis,VerticalAxis,NULL,NULL,hInstance,NULL);
    if(!hWnd){
        MessageBox(NULL,_T("CreateWindow failed!"),_T("Something"),NULL);
        return 1;
    }ShowWindow(hWnd,nCmdShow);
    UpdateWindow(hWnd);
    MSG msg;
    while(GetMessage(&msg,NULL,0,0)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        Sleep(10);
        InvalidateRect(hWnd,NULL,true);
    }return(int)msg.wParam;
}

1 Ответ

0 голосов
/ 30 марта 2020

Это ядро ​​большинства, но не всех, заметного мерцания:

 while(GetMessage(&msg,NULL,0,0)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        Sleep(10);
        InvalidateRect(hWnd,NULL,true);
    }return(int)msg.wParam;

Вы помещаете оператор Sleep и вызов InvalidateRect не только между каждым вызовом WM_PAINT, но и позади КАЖДОГО windows сообщения. Это вызовет все виды проблем. Кроме того, ваш InvalidateRect передает true для флага стирания. Вот улучшение:

Создайте таймер либо в WM_CREATE, либо прямо перед тем, как вы начнете качать сообщения.

SetTimer(hWnd, 999, 33, NULL);  // 999 is just a random timer id, "33" is the milliseconds to wait between WM_TIMER messages

while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}return(int)msg.wParam;

А затем обработайте таймер следующим образом в вашем WndPro c.

case WM_TIMER:
{
    InvalidateRect(hWnd, NULL, FALSE);
    return 0;
}
case WM_DESTROY:
    PostQuitMessage(0);
    return 0;
default:return DefWindowProc(hWnd, message, wParam, lParam);
}

Обратите внимание, что я изменил интервал перерисовки с 10 миллисекунд до 33 миллисекунд. У меня есть монитор 4k, который не может go превышать 60 кадров в секунду. 10 миллисекундный интервал увеличивает это значение до go 100 кадров в секунду. Это еще один элемент мерцания. Поэтому я уменьшил его до 30 кадров в секунду. Мы можем попытаться создать резервную копию sh после того, как пройдем некоторые другие исправления.

И так как мы купили InvalidateRect, не очищая весь экран, мы можем go и добавить это к WndPro c также, чтобы все другие события перерисовки также не стирали его.

case WM_ERASEBKGND:
{
    return 1;
}

На данный момент со всеми внесенными мною изменениями осталось лишь минимальное количество мерцания. особенно когда я изменяю размер вашего окна с полного экрана до четверти моего экрана 4k. Кроме того, когда я создаю ваш код для Release вместо Debug, он еще больше сокращается. Это почти доказывает, что весь математический код между BeginPaint / Endpaint занимает слишком много времени и не рисует кадр в течение 1 кадра.

Еще одна небольшая оптимизация, которую я иногда использую, но не могу однозначно сказать, поможет ли это, - не использовать HD C, предоставляемый вызовом BeginPaint, с которым может быть связана область отсечения. Иногда быстрее получить несжатый HD C и перерисовать его. Вы можете попробовать этот мод для WM_PAINT. YMMV:

case WM_PAINT:
    // validate that HWND
    BeginPaint(hWnd, &ps);
    EndPaint(hWnd, &ps);

    // fetch the unclipped DC so that subsequent drawing operations don't get boundary checked
    hdc = GetDC(hWnd);
    ...
        }if (Draw) {
            SelectPen(hdc, Pens[Quadrilaterals[q$][4]]);
            SelectBrush(hdc, Brushes[Quadrilaterals[q$][5]]);
            Polygon(hdc, Points[q$], 4);
        }q$++;
    }
    ReleaseDC(hWnd, hdc);
    hdc = NULL;
    return 0;

Как я уже говорил выше, большинство, но не все мерцания устранены. Теперь, чтобы избавиться от всего остального, следуйте инструкциям по ссылке, предоставленной Аланом Биртлз . То есть в вашем обработчике WM_PAINT создайте внеэкранную память D C и раскрасьте ее до этого. Затем оторвитесь от экрана за кадром D C до реального окна D C. Ваш модифицированный WM_PAINT должен закрасить все пространство памяти D C, а также фон. Когда вы сделаете это, вы, вероятно, сможете увеличить частоту кадров.

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