(原)关于sdl在部分机器上做视频显示,改变显示窗口大小会崩溃

时间:2023-03-08 20:13:12

今天测试人员反应,之前做的视频绘图显示,会在她机器上,会出现崩溃现象,最后我在她机器上对代码进行跟踪,发现在某种情况,确实会崩溃。

最主要的原因是,视频显示窗口变成非活动窗口的时候,sdl内部会循环消息处理,当处理WM_WINDOWPOSCHANGED消息的时候,就会出现崩溃,崩溃的代码在,D3D_UpdateViewport函数内部的IDirect3DDevice9_SetViewport函数。

当初我为了解决这个问题,觉得是sdl内部接管了窗口的消息处理函数引起的,因为实际上我们这里也没用到sdl的事件处理消息函数,所以我最初是打算把消息接管函数给屏蔽掉,就饿可以解决这个问题,当时也免得消息函数里面出现其他异常情况。

 #ifdef GWLP_WNDPROC
data->wndproc = (WNDPROC) GetWindowLongPtr(hwnd, GWLP_WNDPROC);
if (data->wndproc == WIN_WindowProc) {
data->wndproc = NULL;
} else {
//SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) WIN_WindowProc);//以为屏蔽掉这里的消息处理函数,就可以解决问题了。
}
#else
data->wndproc = (WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC);
if (data->wndproc == WIN_WindowProc) {
data->wndproc = NULL;
} else {
//SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR) WIN_WindowProc);
}
#endif

结果是,崩溃的问题看似解决了,在她那台机器和其他机器上也不会出现崩溃,但有一个其他的问题引入了,就是sdl内部不能响应事件处理函数,这样当我们从视频显示画面变为全屏的时候,出现了一个问题,全屏的时候,画面质量很差,线条和边界画面出现明显的锯齿现象。所以最后此方法没行通,只能再找解决办法:

分析sdl内部消息的源码:

 ///////////////SDL消息分析//////////////////////////
WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)//这个函数是SDL接管windows窗口消息的函数
{
...
case WM_WINDOWPOSCHANGED://会触发这个消息
{
RECT rect;
int x, y;
int w, h; if (!GetClientRect(hwnd, &rect) || IsRectEmpty(&rect)) {
break;
}
ClientToScreen(hwnd, (LPPOINT) & rect);
ClientToScreen(hwnd, (LPPOINT) & rect + ); WIN_UpdateClipCursor(data->window); x = rect.left;
y = rect.top;
//这里先发送窗口移动消息,移动x,y的坐标位置
SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_MOVED, x, y); w = rect.right - rect.left;
h = rect.bottom - rect.top;
//然后这里发送尺寸修改消息,修改显示窗口的w,h
//屏蔽掉这个消息之后,全屏会出现锯齿,视频渲染效果很差
SDL_SendWindowEvent(data->window, SDL_WINDOWEVENT_RESIZED, w,h);//发送这个事件
}
break;
...
} ////////////////////////////////////////////////////
int SDL_SendWindowEvent(SDL_Window * window, Uint8 windowevent, int data1,int data2)
{
case SDL_WINDOWEVENT_MOVED: //处理
if (SDL_WINDOWPOS_ISUNDEFINED(data1) ||
SDL_WINDOWPOS_ISUNDEFINED(data2)) {
return ;
}
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
window->windowed.x = data1;
window->windowed.y = data2;
}
if (data1 == window->x && data2 == window->y) {
return ;
}
window->x = data1;
window->y = data2;
break;
case SDL_WINDOWEVENT_RESIZED://处理
if (!(window->flags & SDL_WINDOW_FULLSCREEN)) {
window->windowed.w = data1;
window->windowed.h = data2;
}
if (data1 == window->w && data2 == window->h) {
return ;
}
window->w = data1;
window->h = data2;
SDL_OnWindowResized(window);//进入这个事件处理会崩溃-lhp
{//注意这个函数里面也是发送一个窗口大小改变的消息
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_SIZE_CHANGED, window->w, window->h);//发送改变消息-LHP
}
break;
/////////////////Break出来以后////////////////////////
if (SDL_GetEventState(SDL_WINDOWEVENT) == SDL_ENABLE) {
SDL_Event event;
event.type = SDL_WINDOWEVENT;
event.window.event = windowevent;
event.window.data1 = data1;
event.window.data2 = data2;
event.window.windowID = window->id; /* Fixes queue overflow with resize events that aren't processed */
if (windowevent == SDL_WINDOWEVENT_RESIZED) {//2)然后触发这个消息事件
SDL_FilterEvents(RemovePendingResizedEvents, &event);
}
if (windowevent == SDL_WINDOWEVENT_SIZE_CHANGED) {//3)最后触发这个消息事件
SDL_FilterEvents(RemovePendingSizeChangedEvents, &event);
}
if (windowevent == SDL_WINDOWEVENT_MOVED) {//1)首先触发这个消息事件
SDL_FilterEvents(RemovePendingMoveEvents, &event);
} posted = (SDL_PushEvent(&event) > );
}
} //////////////////////////////////////
//上面的分析是消息的触发,下面看看消息捕捉以后消息的处理分析
//SDL的渲染事件watch函数用于捕捉事件
static int SDL_RendererEventWatch(void *userdata, SDL_Event *event)
{
SDL_Renderer *renderer = (SDL_Renderer *)userdata;
if (event->type == SDL_WINDOWEVENT) {
SDL_Window *window = SDL_GetWindowFromID(event->window.windowID);
if (window == renderer->window) {
if (renderer->WindowEvent) {
renderer->WindowEvent(renderer, &event->window);
//这个函数的内部实现源码如下:
static void D3D_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event)
{
D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED) {
data->updateSize = SDL_TRUE;
}
}
} if (event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
if (renderer->logical_w) {
UpdateLogicalSize(renderer);
} else {
/* Window was resized, reset viewport */
int w, h; if (renderer->GetOutputSize) {
renderer->GetOutputSize(renderer, &w, &h);
} else {
SDL_GetWindowSize(renderer->window, &w, &h);
} if (renderer->target) {
renderer->viewport_backup.x = ;
renderer->viewport_backup.y = ;
renderer->viewport_backup.w = w;
renderer->viewport_backup.h = h;
} else {
renderer->viewport.x = ;
renderer->viewport.y = ;
renderer->viewport.w = w;
renderer->viewport.h = h;
//这个函数会导致崩溃,在部分机器上,设置视区区域
//最后我把这个地方的这个函数给注释掉了。
//modefy by lhp -20150805
//renderer->UpdateViewport(renderer);//崩溃的地方-LHP
}
}
}
}
} }
//updateViewport
static int D3D_UpdateViewport(SDL_Renderer * renderer)
{
D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata; viewport.X = renderer->viewport.x;
viewport.Y = renderer->viewport.y;
viewport.Width = renderer->viewport.w;
viewport.Height = renderer->viewport.h;
viewport.MinZ = 0.0f;
viewport.MaxZ = 1.0f;
//这里是跟踪的时候,调用崩溃的函数,在部分机器上,当窗口视区改变大小的时候,这个函数会崩溃
IDirect3DDevice9_SetViewport(data->device, &viewport); }

当时想从D3D_UpdateViewport函数入手,但发现我改的几个版本出来的效果,依然会崩溃,例如加clear,getviewport函数看会失败不,等等操作函数,都无用,没办法,只有在消息函数里面处理,把更新显示视区的函数给屏蔽掉。(这个地方做修改也是影响最小的屏蔽,因为这个地方是针对SDL_WINDOWEVENT_SIZE_CHANGED这个事件。)

转载请注明出处:http://www.cnblogs.com/lihaiping/p/4704836.html