12
votes

Gardez la fenêtre active tout en étant traîné (SDL sur Win32)

Au début, mon code configurait l'environnement SDL et a procédé à mettre à jour le contexte OpenGL, sans effectuer de traitement SDL_Event. Cela provoque la fenêtre, tant qu'elle était ouverte, apparaître à Windows ne répond pas. La fenêtre scintille un peu. La barre de titre obtiendrait "(ne répondant pas)" l'a annoncée et en cliquant à l'intérieur de la fenêtre, il devient grisé, car Windows le fait par défaut sur des fenêtres non réactives. Toutefois, dans cet état (même comme et après qu'il devienne grisé), l'écran OpenGL continue de mettre à jour et d'animer, et voici le kicker, il ne le fait même pas pendant que la fenêtre est traînée . Clairement, dans ce cas, l'application ne manipule pas les événements de Windows correctement, ce qui oblige les fenêtres à penser qu'il est dans un état suspendu. Mais il est évident que l'OpenGL continue de rendre.

Maintenant, je fais une seule modification au code, qui correspond à ces trois lignes placées dans un endroit approprié à l'intérieur de la boucle (qui fait également le dessin OpenGL): xxx

Tout cela fait est de rincer la file d'attente de messages à l'aide de SDL.

Le comportement est que Windows ne pense plus qu'il "ne répond pas" et qu'il ne se griette pas. Pas de scintillement. Tout semble courir à la mer. Mais une fois que je clique et faites glisser la barre de titre pour faire glisser la fenêtre, le rendu est bloqué. Je n'ai pas débuté pour être sûr, mais je soupçonne que SDL_Pollevent Blocks pour la durée de la traînée de la fenêtre.

Y a-t-il un moyen autour de cela? C'est intéressant car une partie du comportement exposé par omet de gérer les événements est la preuve que ce que je veux est possible en théorie.

mise à jour: j'ai trouvé ce fil: http: //www.gamev. net / thème / 488074-Win32-Message-pompe-pompe-and-OpenGL --- pause-rendu - tandis que-glissage extraire /

Le verdict semble être que cela revient à certains choix que Microsoft a fait pour nous ... Il est essentiellement coincé dans defwindowproc () jusqu'à ce que la souris soit libérée. Il serait très désordonné de pirater une solution pour cela et je pourrais peut-être faire un travail autour d'un autre fil. Mais je ne veux même pas commencer à penser à jongler un contexte opengl de plusieurs threads, si c'est même quelque chose que possible.


1 commentaires

Il peut s'agir d'un bug dans SDL: bugzilla.libsdl.org/show_bug.cgi?id = 2077


5 Réponses :


0
votes

Je propose que vous avez créé 2 threads:

  • fil 1: boucles appelant sdl_polleventvent () (sans rien rendu)
  • thread 2: Le rendu OpenGL (sans appeler sdl_polleventventvent ())

    De cette façon, votre contexte OpenGL serait manipulé à partir d'un seul fil. Toute la solution a un impact minimum sur l'architecture de votre application.


8 commentaires

Merci pour votre réponse, je vais chercher cela.


FYI Soyez prudent - OpenGL n'est pas un fil sûr. Je ne sais pas comment SDL gère les événements assez bien pour dire si des événements sont assez étroitement liés au contexte OpenGL pour causer des problèmes.


Ce n'est pas une bonne solution car le rendu dépend du résultat de la manipulation des événements. Avoir deux threads de tâches non parallélisables n'aide pas.


@imallett Vous n'avez pas besoin d'OpenGL pour être en sécurité, puisque vous faites toujours des appels OpenGL à partir d'un seul fil.


@philix Bien sûr, vous auriez besoin de synchroniser correctement les deux threads. Vous pouvez utiliser une serrure en lecture-écriture, le verrou de lecture étant principalement maintenu par votre thread OpenGL, tandis que le verrouillage de l'écriture n'est parfois que détenu par la boucle d'événement. Si j'ai un peu de temps, je pourrais écrire du code pour le démontrer.


@ user1202136 Le verrouillage serait principalement sérialisé les deux tâches. Une idée plus propre serait de gérer asynchroneusement les événements.


@ user1202136 Je n'ai pas dit que c'était impossible , juste que l'on devrait Soyez prudent . Après avoir écrit ma propre couche de fenêtrage, je peux vous dire qu'il y a des problèmes subtils ici. Par exemple, est l'API WGL / GLX pour lier une partie de contexte de OpenGL ou fait-il partie du système de fenêtres? Le rappel d'entrée du système de fenêtrage est-il par cadre ou par contexte (gardant à l'esprit que les contextes de l'appareil! = Contents de rendez-vous! = Contextes matériels)? [a continué]


La question sous-jacente de l'OP est que sous Windows, en faisant glisser les cadres, comme dimensionnez la fenêtre et bloque d'autres événements d'entrée. Pour maintenir le rendu et la réactivité, il faut redessiner le cadre alors qu'il est glissé (pouvant être obtenu en appelant directement à partir du rappel d'entrée ou à l'aide d'un fil) .¶ Votre solution fonctionnera (de même que A +1 pour vous ramener à 0, pour correspondre à l'autre réponse de threading), mais je pense que la question soulevée par @philix est importante. Néanmoins, presque tous les autres systèmes de fenêtres font quelque chose comme ça en interne (Gah), en utilisant un minuteur + fil pour invoquer le rendu.



0
votes

De nombreuses procédures de Windows exécutent une boucle de message distincte jusqu'à ce qu'un certain événement se produise, vous ne devriez donc pas compter sur votre boucle principale pour faire le dessin. Si possible, la logique d'application et le rendu doivent toujours être traités dans un fil séparé.

Votre fil principal (qui ne gère que le traitement des messages) n'a pas besoin de context GL. Vous n'avez donc pas besoin de vous inquiéter de partage.


0 commentaires

4
votes

Quelques partenaires de contournement qui fonctionne pour moi - ajoutez un filtre d'événements pour sdl_windowevent_size_changeed Event et effectuez un cadre StripViewPort et Structure supplémentaires.

int SDLApp::eventFilter(void* pthis, const SDL_Event *event)
{
    if (event->type == SDL_WINDOWEVENT &&
        event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
    {
        SDLApp* app = (SDLApp*)pthis;
        // Note: NULL rectangle is the entire window
        SDL_RenderSetViewport(app->renderer_, NULL);
        app->DrawFrame();
    }
    return 1;
}

...
SDL_SetEventFilter((SDL_EventFilter)SDLApp::eventFilter, this);


0 commentaires

1
votes

J'ai eu un problème similaire dans lequel il gèlerait la lecture vidéo lorsque la fenêtre a été glissée ou redimensionnée. La solution que j'ai trouvée était de reproduire un fil séparé pour le rendu et d'utiliser le fil principal pour l'entrée.

Exemple: P>

DWORD RenderThread(SDL_Window* window)
{
    //Rendering stuff here...
}

int main()
{
    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_Window* window = SDL_CreateWindow("Title Here",
        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, h, w, SDL_WINDOW_RESIZABLE);

    HANDLE hRenderThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RenderThread, window, 0, NULL);

    SDL_Event event;

    while (1)
    {
        SDL_PollEvent(&event);

        switch (event.type)
        {
            //Event handling here...
        }
    }
}


0 commentaires

3
votes

Cette question est vieille, mais la solution que j'utilise ne semble pas être mentionnée nulle part ailleurs, alors c'est ici.

J'ai eu mon inspiration de Cette réponse, et il n'utilise pas de threads supplémentaires. xxx

Bien sûr, si votre fonction de rendu doit conserver Etat (par exemple, si vous utilisez OOP), utilisez le paramètre Void * de evendwatch (vide *, sdl_event *) pour transmettre l'état.


1 commentaires

Il convient de noter que les manutentionnaires Eventwatch fonctionnent sur le fil qui invoque l'événement, plutôt que le fil principal. Cela pourrait conduire à des problèmes car SDL s'attend à ce que le rendu effectué sur le thread principal, de sorte que votre appel à rendu pourrait être invalide.