首先,我的代码设置了SDL环境,然后继续更新OpenGL上下文,而不执行任何SDL_Event处理。这会导致窗口在打开时对Windows显示无响应。窗口闪烁了一下。标题栏将附加“(无响应)”,并且在窗口内单击时,它将变为灰色,因为Windows在非响应窗口上默认执行此操作。然而,在这种状态下(即使在它变灰之后),OpenGL显示继续更新和动画,这里是踢球者,它甚至在拖动窗口的时候 。显然,在这种情况下,应用程序不会正确处理来自窗口的事件,导致窗口认为它处于挂起状态。但有明显的证据表明,opengl仍在继续呈现。
现在我对代码进行一次修改,这三行放在循环内的适当位置(也可以进行OpenGL绘制):
SDL_Event event;
if (SDL_PollEvent(&event) && event.type == SDL_QUIT)
break;
所有这一切都是使用SDL刷新消息队列。
现在的行为是Windows不再认为它是“无响应”并且它不会变灰。没有闪烁。一切似乎都在游泳。但是,一旦我单击并拖动标题栏拖动窗口,渲染就会被阻止。我没有调试它以确定,但我怀疑SDL_PollEvent阻止窗口拖动的持续时间。
有解决方法吗?这很有趣,因为未能处理事件所表现出的部分行为证明了我理想的可能性。
更新:我找到了这个主题:http://www.gamedev.net/topic/488074-win32-message-pump-and-opengl---rendering-pauses-while-draggingresizing/
判决似乎是微软为我们做出的某些选择......它基本上被卡在DefWindowProc()
中,直到鼠标被释放。破解修复程序会变得非常混乱,我可能可以通过在另一个线程中渲染来解决问题。但我甚至不想开始考虑从多个线程中处理OpenGL上下文,如果这甚至是可能的话。
答案 0 :(得分:3)
适用于我的一些解决方法 - 为SDL_WINDOWEVENT_SIZE_CHANGED事件添加事件过滤器,并执行其他SetViewport和绘制框架。
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);
答案 1 :(得分:2)
这个问题很旧,但是我正在使用的解决方案似乎在其他任何地方都没有提及,所以在这里。
我从this的答案中得到了启发,它不使用其他线程。
#include <SDL.h>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <SDL_syswm.h>
#define SIZE_MOVE_TIMER_ID 1
bool sizeMoveTimerRunning = false;
int eventWatch(void*, SDL_Event* event) {
if (event->type == SDL_SYSWMEVENT) {
const auto& winMessage = event->syswm.msg->msg.win;
if (winMessage.msg == WM_ENTERSIZEMOVE) {
// the user started dragging, so create the timer (with the minimum timeout)
// if you have vsync enabled, then this shouldn't render unnecessarily
sizeMoveTimerRunning = SetTimer(GetActiveWindow(), SIZE_MOVE_TIMER_ID, USER_TIMER_MINIMUM, nullptr);
}
else if (winMessage.msg == WM_TIMER) {
if (winMessage.wParam == SIZE_MOVE_TIMER_ID) {
// call your render function
render();
}
}
}
return 0;
}
// rendering function
void render() {
/* do your rendering here */
}
// event loop - call this function after setting up your window to start the event loop
void eventLoop() {
SDL_AddEventWatch(eventWatch, nullptr); // register the event watch function
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); // we need the native Windows events, so we can listen to WM_ENTERSIZEMOVE and WM_TIMER
while (true) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (sizeMoveTimerRunning) {
// modal drag/size loop ended, so kill the timer
KillTimer(GetActiveWindow(), SIZE_MOVE_TIMER_ID);
sizeMoveTimerRunning = false;
}
/* handle the events here */
}
render();
}
}
当然,如果渲染功能需要保持其他状态(例如,如果您使用的是OOP),请使用void*
的{{1}}参数传递状态。
答案 2 :(得分:0)
我建议你创建2个帖子:
这样,您的OpenGL上下文将从单个线程进行操作。整个解决方案对应用程序的体系结构影响最小。
答案 3 :(得分:0)
许多Windows程序运行单独的消息循环,直到发生某个事件,因此您不应该依赖主循环来进行绘制。如果可能,应始终在单独的线程中处理应用程序逻辑和呈现。
您的主线程(仅处理消息处理)根本不需要GL上下文,因此您无需担心共享。
答案 4 :(得分:0)
我遇到了类似的问题,当拖动或调整窗口大小时,它会冻结视频播放。我发现的解决方案是生成一个单独的线程进行渲染,并使用主线程进行输入。
示例:
{{1}}
请记住,您必须在执行事件处理的线程中创建窗口。如果不是它将无法正常工作。您可以在事件处理线程中创建窗口,然后将该窗口指针传递给渲染线程。