Toon posts:

[Win32/Direct3D] Message loop probleem

Pagina: 1
Acties:

Verwijderd

Topicstarter
Hallo,

Het volgende is mijn probleem: Ik heb een prachtige messageloop. Als mijn programma, wat direct 3d gebruikt actief, is er niets aan de hand. Maar als ik hem minimaliseer of overschakel naar een ander programma loopt windows plotseling heel erg traag, het duurt 10 seconden voordat hij op mij reageert.
Als ik direct 3d functie Clear(.....) uitschakel, dan werkt het weer prima, wat is er aan de hand?

Mij code:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
    // Message loop uit de main
    MSG msg;
    BOOL HasMsg;
    do
    {
        // Kijken bericht of er een bericht is, zo ja die ophalen
        if (Active == false)
            HasMsg = GetMessage(&msg, NULL, 0, 0);  
        else
            HasMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);  
                
        if (HasMsg)
        {
            // Het bericht behandelen
            TranslateMessage(&msg); 
            DispatchMessage(&msg);

        }
        // Geen bericht dus wij kunnen wat doen
        else
        { 
            Render();
        }
    }
    while (msg.message != WM_QUIT);

// Render functie
void Render()
{
    // Maakt het backbuffer leeg
    pD3D_Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    
    if (FAILED(pD3D_Device->BeginScene())) return;

    pD3D_Device->EndScene();

    // Zorgt dat de backbuffer primarybuffer wordt (flip)
    pD3D_Device->Present(NULL, NULL, NULL, NULL);
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

als je die clear nou eens ONDER de BeginScene zet :)

Niet dat dat nodig is, maar als je window geminimalizeerd is dan is je device lost (dacht ik), en aangezien BeginScene dat aangeeft, kun je die clear er beter onder zetten, zodat ie niet wordt uitgevoerd als ie geminimalizeerd is...

Een betere methode is overigens detecteren dat ie geminimalizeerd wordt/is, en zo ja gewoon niet Render () aanroepen

waar staat die bool Active eigenlijk voor?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
Ik gok dat daarmee aangegeven moet worden of de applicatie actief is of niet. Niet actief is gelijk aan geminimaliseerd.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

Op maandag 21 januari 2002 23:24 schreef The - DDD het volgende:
Ik gok dat daarmee aangegeven moet worden of de applicatie actief is of niet. Niet actief is gelijk aan geminimaliseerd.
dat dacht ik eerst ook, maar aangezien hij Render () altijd aanroept, ongeacht de waarde van Active, vind ik het nogal onwaarschijnlijk dat het daar idd voor staat :)

.edit: oh wacht nou ik er nog een keer naar kijk, zie ik idd dat het daar best wel voor zou kunnen staan, aangezien als ie niet Active is GetMessage wordt aangeroepen, die wacht tot er een msg is, dus hasMsg is dan altijd true. Wel een vage constructie hoor :D

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
Ja ondanks dat je window niet actief is, worden er nog tering veel berichten nar je window gestuurd als hij geminimaliseerd is.

Beste is om bij minimalisatie de render uit je loop te halen, maar natuurlijk wel je scene updates te blijven doen.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

nee Render () wordt niet aangeroepen als er berichten zijn, dus de enige fout die ik kan bedenken is dat de waarde van Active niet klopt (altijd true is dus, ook al is ie geminimalizeerd)

offtopic:
kom je ook in [topic=387951/1/100] , harsikke gezellig :P

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    // Message loop uit de main
    MSG msg;
    BOOL HasMsg;
    do
    {
        // Kijken bericht of er een bericht is, zo ja die ophalen
        if (Active == false)
            HasMsg = GetMessage(&msg, NULL, 0, 0);  
        else
            HasMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);  
                
        if (HasMsg)
        {
            // Het bericht behandelen
            TranslateMessage(&msg); 
            DispatchMessage(&msg);

        }
        // Geen bericht dus wij kunnen wat doen
        else
        {
            if(minimized)
            {
                Render();
            }
        }
    }
    while (msg.message != WM_QUIT);

// Render functie
void Render()
{
    if (FAILED(pD3D_Device->BeginScene())) return;

    // Maakt het backbuffer leeg
    pD3D_Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);


    pD3D_Device->EndScene();

    // Zorgt dat de backbuffer primarybuffer wordt (flip)
    pD3D_Device->Present(NULL, NULL, NULL, NULL);
}

Die minimized verander je uiteraard bij een WM_SIZE
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
    case WM_SIZE:
        {
            if (wParam == SIZE_MINIMIZED)
            {
                minimized = true;
                break;
            }
            else
            {
                minimized = false;
            }
            // ...
        }

De Break gooit je hier uit de message handling omdat je zodoende voorkomt dat er allerlij nutteloze zaken worden gedaan, resizen hoeft immers niet, je window is niet zichtbaar.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

aargh nee wat stom, GetMessage geeft niet terug of er een msg is (want er is tenslotte altijd een message als ie retourneert), GetMessage retourneert 0 als de msg WM_QUIT was, en anders niet 0 |:(

daar zit de fout dus :)

maw, dit:
code:
1
2
3
4
if (Active == false)
    HasMsg = GetMessage(&msg, NULL, 0, 0);  
else
    HasMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

veranderen in:
code:
1
2
3
4
5
6
7
if (Active == false)
{
    GetMessage(&msg, NULL, 0, 0);
    HasMsg = TRUE;
}
else
    HasMsg = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
Inderdaad wel eng ja, wat is het gevolg van meerdere keren een getmessage aanroepen zonder tussentijds translate en dispatch te doen?

Als ik het goed zie moet die hele getmessage verwijderd worden, je kan het met alleen PeekMessage af.
MSDN zegt over de return van PeekMessage
Return Values
If a message is available, the return value is nonzero.

If no messages are available, the return value is zero.

  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
Zoiets dus:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while(TRUE)
{
    if(PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))       // if there is a message, do something with it.
    {
        if (msg.message == WM_QUIT)
            break;

        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
    else
    {
        engine->SceneUpdate(timer->GetElapsedSeconds(1));
    }
}

Rechtstreeks uit mijn 3D ding geripped.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

ja maar het is een beetje loos om PeekMessage te gebruiken als je toch op een message zit te wachten... dan verbruik je alleen maar processortijd omdat je thread door blijft lopen in een loopje (in tegenstelling tot bij GetMessage, waar je thread in waiting wordt gezet als er geen message is, en hij wordt weer active zodra er een message binnen komt)

en je vergeet die Active te gebruiken :)

persoonlijk vind ik dan de code van de topicstarter beter, hoe wazig ie ook in elkaar zit :D

overigens, van die if (PeekMessage) moet je een while (PeekMessage) maken, zodat je meerdere messages in een keer kunt pakken (anders is bijvoorbeeld je muisbeweging nogal jerky en traag, omdat ie maar 1 muisbericht per frame binnen krijgt, terwijl er meerdere per frame gegenereerd worden) (en ja ik heb dat hardhandig ondervonden :Y))

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
Zo beter dan:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
while(TRUE)
{
    if (active)
    {
        if(PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))       // if there is a message, do something with it.
        {
            if (msg.message == WM_QUIT)
                break;

            TranslateMessage (&msg);
            DispatchMessage (&msg);
        }
        else
        {
            engine->SceneUpdate(timer->GetElapsedSeconds(1));
        }
    }
    else
    {
        GetMessage (&msg, NULL, 0, 0);
        if (msg.message == WM_QUIT)
            break;

        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

zie mijn edit :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
Uhm die peekmessage blijft herhalen totdat er geen messages meer zijn, dan wordt er pas weer gerenderd, kijk maar is goed.

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 00:42

.oisyn

Moderator Devschuur®

Demotivational Speaker

oh ja :D

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • The - DDD
  • Registratie: Januari 2000
  • Laatst online: 03-09 16:40
Wel wakker blijven he... :Z

Ik ga nu :z

Verwijderd

Topicstarter
Hee, bedankt voor jullie hulp. Ik zal even uitleggen wat de bedoeling van mijn messageloop is.

Ik wil dat hij de idle time gebruikt voor het renderen, dus als het programma niet geminimaliseerd is dan vreet hij alle processor kracht.
Als je hem deactiveert moet hij nog steeds renderen en moet hij nog steeds de idle time gebruiken, omdat als je een ander venster boven mijn rendervenster zet, dan moet hij natuurlijk doorrenderen. Een mooi effect is dan als je op een knop drukt (= een ander venster) dat ie dan stopt met renderen.
Dus alleen als het programma geminimaliseerd is dan moet hij stoppen met renderen.
Dat is inderdaad die variabele Active. (dat word in de wndproc geregeld), Ik heb gecontroleerd als dat werkt.
In mijn 'echte' programma rendert hij niet wanneer hij niet Active is (|:( vergeten hier in de code te zetten).

Bij de minimalisatie werkt het programma prima, het gaat dus alleen om het deactiveren dus als je een ander venster activeert.

Verwijderd

Op dinsdag 22 januari 2002 09:53 schreef BobKid het volgende:
Hee, bedankt voor jullie hulp. Ik zal even uitleggen wat de bedoeling van mijn messageloop is.
Ja doe dat, want het is niet zo duidelijk.
Ik wil dat hij de idle time gebruikt voor het renderen, dus als het programma niet geminimaliseerd is dan vreet hij alle processor kracht.
Dat doe je door:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void
KernelLoop()
{
    bool    bQuitMainLoop;
    MSG     mMsg;

    // check if there are messages in the queue. If so: process them.
    for(bQuitMainLoop=false;!bQuitMainLoop;)
    {
        while(PeekMessage(&mMsg, NULL, 0, 0, PM_NOREMOVE)) 
        {
            if(GetMessage(&mMsg, NULL, 0, 0)) 
            {
                TranslateMessage(&mMsg);
                DispatchMessage(&mMsg);
            }
            else
            {
                bQuitMainLoop=true;
                break;
            }
        }
        // we're displaying content. That needs to be as smooth as possible so no messagewaitin'
        if(m_gpDemoDat->GetSystemState()==SSTATE_ARUN_KERNELLOOP)
        {
            ExecuteTimeLine((long)(m_gpDemoDat->GetfElapsedTimeInSecs() * 1000));
            m_gpDemoDat->CalcElapsedTimeInSeconds();
            RenderFrame();
        }
        else
        {
            WaitMessage();  
        }
    }
    //////////////////
    // a WM_QUIT message has been received.
    //////////////////

    // set new system state.
    m_gpDemoDat->SetSystemState(SSTATE_ARUN_POSTKERNELLOOP);

    // Control should be given back to the caller's main routine, but we first have to
    // do some cleanup of handles and windows. 
    CleanUpKernelInternals();
}
Als je hem deactiveert moet hij nog steeds renderen en moet hij nog steeds de idle time gebruiken, omdat als je een ander venster boven mijn rendervenster zet, dan moet hij natuurlijk doorrenderen. Een mooi effect is dan als je op een knop drukt (= een ander venster) dat ie dan stopt met renderen.
Zet de prioriteit van je mainthread op 'low'. Zodoende krijgt jouw tooltje alleen de CPU wanneer er niets gebeurt op je system. (wat je volgens mij wilt). Wanneer er een venster compleet jouw rendervenster bedekt, kun je renderen wat je wilt, maar er wordt niets gerenderd. Dit komt omdat de GDI de client area van jouw window niet invalid zal marken en de videodriver daardoor niet in de framebuffer jouw graphics zal laten zien. Heeft dus geen zin. Alleen de onbedekte areas worden gerenderd door de videodriver.
Dus alleen als het programma geminimaliseerd is dan moet hij stoppen met renderen. Dat is inderdaad die variabele Active. (dat word in de wndproc geregeld), Ik heb gecontroleerd als dat werkt. In mijn 'echte' programma rendert hij niet wanneer hij niet Active is (|:( vergeten hier in de code te zetten). Bij de minimalisatie werkt het programma prima, het gaat dus alleen om het deactiveren dus als je een ander venster activeert.
Vang messages op: WM_ACTIVATE, die wordt gepost bij zowel activeren als deactiveren, WM_SIZE, die wordt gepost wanneer het window wordt geminimized of gemaximized.

Je mainloop rent in de rondte, tune die loop zodat hij alleen doet wat hij moet doen op de tijdstippen die jij bepaalt, dmv de handling van die messages.

De threadpriority zet je door:
code:
1
    bResult = SetThreadPriority(hOwnThread,THREAD_PRIORITY_LOWEST);
Pagina: 1