From bfc5300e57ac0cc0d8bb5740fb59b2b6e59a6fc8 Mon Sep 17 00:00:00 2001 From: cxgeorge <> Date: Tue, 24 Jul 2001 23:27:52 +0000 Subject: [PATCH] update fullscreen, alt-tab, error handling --- panda/src/dxgsg/dxGraphicsStateGuardian.cxx | 327 ++++++++-------- panda/src/dxgsg/dxGraphicsStateGuardian.h | 5 +- panda/src/wdxdisplay/wdxGraphicsWindow.cxx | 398 +++++++++++++------- panda/src/wdxdisplay/wdxGraphicsWindow.h | 5 + 4 files changed, 443 insertions(+), 292 deletions(-) diff --git a/panda/src/dxgsg/dxGraphicsStateGuardian.cxx b/panda/src/dxgsg/dxGraphicsStateGuardian.cxx index 14fb261a5a..0db41b74d9 100644 --- a/panda/src/dxgsg/dxGraphicsStateGuardian.cxx +++ b/panda/src/dxgsg/dxGraphicsStateGuardian.cxx @@ -306,6 +306,9 @@ init_dx( LPDIRECTDRAW7 context, _d3d = pD3D; _d3dDevice = pDevice; _view_rect = viewrect; + + _last_testcooplevel_result = S_OK; + HRESULT hr; if(dx_show_fps_meter) { @@ -500,8 +503,8 @@ init_dx( LPDIRECTDRAW7 context, _d3dDevice->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTFP_POINT); _d3dDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTFP_NONE); _d3dDevice->SetTextureStageState(0, D3DTSS_MAXANISOTROPY,_CurTexAnisoDegree); - _d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSU,get_texture_wrap_mode(_CurTexWrapModeU)); - _d3dDevice->SetTextureStageState(0,D3DTSS_ADDRESSV,get_texture_wrap_mode(_CurTexWrapModeV)); + _d3dDevice->SetTextureStageState(0, D3DTSS_ADDRESSU,get_texture_wrap_mode(_CurTexWrapModeU)); + _d3dDevice->SetTextureStageState(0, D3DTSS_ADDRESSV,get_texture_wrap_mode(_CurTexWrapModeV)); ta->issue(this); // no curtextcontext, this does nothing. dx should already be properly inited above anyway @@ -567,8 +570,7 @@ clear(const RenderBuffer &buffer) { HRESULT hr = _d3dDevice->Clear(0, NULL, flags, clear_colr, (D3DVALUE) _depth_clear_value, (DWORD)_stencil_clear_value); if (hr != DD_OK) - dxgsg_cat.error() - << "dxGSG::clear_buffer failed: Clear returned " << ConvD3DErrorToString(hr) << endl; + dxgsg_cat.error() << "clear_buffer failed: Clear returned " << ConvD3DErrorToString(hr) << endl; /* The following line will cause the background to always clear to a medium red _color_clear_value[0] = .5; /* The following lines will cause the background color to cycle from black to red. @@ -653,15 +655,14 @@ prepare_display_region() { } } - - +#if 0 //////////////////////////////////////////////////////////////////// // Function: set_clipper // Access: // Description: Useless in DX at the present time //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian::set_clipper(RECT cliprect) { -#if 0 + LPDIRECTDRAWCLIPPER Clipper; HRESULT result; @@ -699,8 +700,8 @@ void DXGraphicsStateGuardian::set_clipper(RECT cliprect) { } free(rgn_data); DeleteObject(hrgn); -#endif } +#endif //////////////////////////////////////////////////////////////////// // Function: DXGraphicsStateGuardian::render_frame @@ -711,9 +712,10 @@ void DXGraphicsStateGuardian::set_clipper(RECT cliprect) { //////////////////////////////////////////////////////////////////// void DXGraphicsStateGuardian:: render_frame(const AllAttributesWrapper &initial_state) { - if (!_dx_ready) return; + if (!_dx_ready) + return; - _win->begin_frame(); + _win->begin_frame(); _lighting_enabled_this_frame = false; #ifdef DO_PSTATS @@ -730,7 +732,20 @@ render_frame(const AllAttributesWrapper &initial_state) { set_state(state, false); #endif - _d3dDevice->BeginScene(); + HRESULT hr = _d3dDevice->BeginScene(); + + if(FAILED(hr)) { + if((hr==DDERR_SURFACELOST)||(hr==DDERR_SURFACEBUSY)) { + if(dxgsg_cat.is_debug()) + dxgsg_cat.debug() << "BeginScene returns " << ConvD3DErrorToString(hr) << endl; + + CheckCooperativeLevel(); + } else { + dxgsg_cat.error() << "BeginScene failed, unhandled error hr == " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + return; + } /* _d3dDevice->SetTransform(D3DTRANSFORMSTATE_VIEW, &matIdentity); */ @@ -780,9 +795,9 @@ render_frame(const AllAttributesWrapper &initial_state) { // Now we're done with the frame processing. Clean up. - _d3dDevice->EndScene(); // FPS meter drawing MUST occur after EndScene, since it uses GDI + hr = _d3dDevice->EndScene(); // FPS meter drawing MUST occur after EndScene, since it uses GDI - if (_lighting_enabled_this_frame) { + if (_lighting_enabled_this_frame) { // Let's turn off all the lights we had on, and clear the light // cache--to force the lights to be reissued next frame, in case // their parameters or positions have changed between frames. @@ -803,9 +818,22 @@ render_frame(const AllAttributesWrapper &initial_state) { // ideal--there may be a better way. Maybe if the lights were just // more aware of whether their parameters or positions have changed // at all? - } + } - if(dx_show_fps_meter) { + if(FAILED(hr)) { + if((hr==DDERR_SURFACELOST)||(hr==DDERR_SURFACEBUSY)) { + if(dxgsg_cat.is_debug()) + dxgsg_cat.debug() << "EndScene returns " << ConvD3DErrorToString(hr) << endl; + + CheckCooperativeLevel(); + } else { + dxgsg_cat.error() << "EndScene failed, unhandled error hr == " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + return; + } + + if(dx_show_fps_meter) { DO_PSTATS_STUFF(PStatTimer timer(_win->_show_fps_pcollector)); DWORD now = timeGetTime(); // this is win32 fn @@ -1056,7 +1084,7 @@ render_subgraph(RenderTraverser *traverser, // We infer the modelview matrix by doing a wrt on the projection // node. LMatrix4f modelview_mat; - get_rel_mat(subgraph, _current_projection_node, modelview_mat); // reversed from GL + get_rel_mat(subgraph, _current_projection_node, modelview_mat); //needs reversal from glgsg, probably due D3D LH coordsys // get_rel_mat(_current_projection_node, subgraph, modelview_mat); if (_coordinate_system != CS_yup_left) { @@ -1142,7 +1170,7 @@ void INLINE TestDrawPrimFailure(DP_Type dptype,HRESULT hr,LPDIRECTDRAW7 pDD,DWOR // loss of exclusive mode is not a real DrawPrim problem HRESULT testcooplvl_hr = pDD->TestCooperativeLevel(); if((testcooplvl_hr != DDERR_NOEXCLUSIVEMODE)||(testcooplvl_hr != DDERR_EXCLUSIVEMODEALREADYSET)) { - dxgsg_cat.fatal() << ((dptype==DrawPrimStrided) ? "DrawPrimStrided" : "DrawPrim") << "failed: result = " << ConvD3DErrorToString(hr) << endl; + dxgsg_cat.fatal() << ((dptype==DrawPrimStrided) ? "DrawPrimStrided" : "DrawPrim") << " failed: result = " << ConvD3DErrorToString(hr) << endl; } exit(1); } @@ -5488,25 +5516,27 @@ dx_setup_after_resize(RECT viewrect, HWND mwindow) { HRESULT hr; if (FAILED(hr = _pDD->CreateSurface( &ddsd, &_pri, NULL ))) { - dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for primary : result = " << ConvD3DErrorToString(hr) << endl; + dxgsg_cat.fatal() << "resize() - CreateSurface failed for primary : result = " << ConvD3DErrorToString(hr) << endl; exit(1); } - // Create a clipper object which handles all our clipping for cases when - // our window is partially obscured by other windows. - LPDIRECTDRAWCLIPPER Clipper; - - if (FAILED(hr = _pDD->CreateClipper( 0, &Clipper, NULL ))) { - dxgsg_cat.fatal() - << "dxgsg - CreateClipper after resize failed : result = " << ConvD3DErrorToString(hr) << endl; - exit(1); + if(!dx_full_screen) { + // Create a clipper object which handles all our clipping for cases when + // our window is partially obscured by other windows. + LPDIRECTDRAWCLIPPER Clipper; + + if (FAILED(hr = _pDD->CreateClipper( 0, &Clipper, NULL ))) { + dxgsg_cat.fatal() + << "CreateClipper after resize failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + // Associate the clipper with our window. Note that, afterwards, the + // clipper is internally referenced by the primary surface, so it is safe + // to release our local reference to it. + Clipper->SetHWnd( 0, mwindow ); + _pri->SetClipper( Clipper ); + Clipper->Release(); } - // Associate the clipper with our window. Note that, afterwards, the - // clipper is internally referenced by the primary surface, so it is safe - // to release our local reference to it. - Clipper->SetHWnd( 0, mwindow ); - _pri->SetClipper( Clipper ); - Clipper->Release(); // Recreate the backbuffer. (might want to handle failure due to running out of video memory) @@ -5516,7 +5546,7 @@ dx_setup_after_resize(RECT viewrect, HWND mwindow) { PRINTVIDMEM(_pDD,&ddsd_back.ddsCaps,"resize backbuffer surf"); if (FAILED(hr = _pDD->CreateSurface( &ddsd_back, &_back, NULL ))) { - dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for backbuffer : result = " << ConvD3DErrorToString(hr) << endl; + dxgsg_cat.fatal() << "resize() - CreateSurface failed for backbuffer : result = " << ConvD3DErrorToString(hr) << endl; exit(1); } @@ -5524,20 +5554,20 @@ dx_setup_after_resize(RECT viewrect, HWND mwindow) { // Recreate and attach a z-buffer. if (FAILED(hr = _pDD->CreateSurface( &ddsd_zbuf, &_zbuf, NULL ))) { - dxgsg_cat.fatal() << "DXGraphicsStateGuardian::resize() - CreateSurface failed for Z buffer: result = " << ConvD3DErrorToString(hr) << endl; + dxgsg_cat.fatal() << "resize() - CreateSurface failed for Z buffer: result = " << ConvD3DErrorToString(hr) << endl; exit(1); } // Attach the z-buffer to the back buffer. if ((hr = _back->AddAttachedSurface( _zbuf ) ) != DD_OK) { dxgsg_cat.fatal() - << "DXGraphicsStateGuardian::resize() - AddAttachedSurface failed : result = " << ConvD3DErrorToString(hr) << endl; + << "resize() - AddAttachedSurface failed : result = " << ConvD3DErrorToString(hr) << endl; exit(1); } if ((hr = _d3dDevice->SetRenderTarget(_back,0x0) ) != DD_OK) { dxgsg_cat.fatal() - << "DXGraphicsStateGuardian::resize() - SetRenderTarget failed : result = " << ConvD3DErrorToString(hr) << endl; + << "resize() - SetRenderTarget failed : result = " << ConvD3DErrorToString(hr) << endl; exit(1); } @@ -5546,7 +5576,7 @@ dx_setup_after_resize(RECT viewrect, HWND mwindow) { hr = _d3dDevice->SetViewport( &vp ); if (hr != DD_OK) { dxgsg_cat.fatal() - << "DXGraphicsStateGuardian:: SetViewport failed : result = " << ConvD3DErrorToString(hr) << endl; + << "SetViewport failed : result = " << ConvD3DErrorToString(hr) << endl; exit(1); } } @@ -5571,13 +5601,15 @@ HRESULT DXGraphicsStateGuardian::RestoreAllVideoSurfaces(void) { // note: could go through and just restore surfs that return IsLost() true // apparently that isnt as reliable w/some drivers tho if (FAILED(hr = _pDD->RestoreAllSurfaces() )) { - dxgsg_cat.fatal() - << "DXGraphicsStateGuardian:: RestoreAllSurfs failed : result = " << ConvD3DErrorToString(hr) << endl; - return hr; + dxgsg_cat.fatal() << "RestoreAllSurfs failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); } // cant access template in libpanda.dll directly due to vc++ limitations, use traverser to get around it traverse_prepared_textures(refill_tex_callback,this); + + if(dxgsg_cat.is_debug()) + dxgsg_cat.debug() << "restore and refill of video surfaces complete...\n"; return S_OK; } @@ -5627,56 +5659,15 @@ void DXGraphicsStateGuardian::show_full_screen_frame(void) { // waiting for vsync? hr = _pri->Flip( NULL, dwFlipFlags); - if(hr == DDERR_SURFACELOST || hr == DDERR_SURFACEBUSY) { - //full screen app has been switched away - HRESULT hr; - - // TestCooperativeLevel returns DD_OK: If the current mode is same as the one which the App set. - // The following error is returned only for exclusivemode apps. - // DDERR_NOEXCLUSIVEMODE: Some other app took exclusive mode. - hr = _pDD->TestCooperativeLevel(); - - while(hr == DDERR_NOEXCLUSIVEMODE) { - // This means that mode changes had taken place, surfaces - // were lost but still we are in the original mode, so we - // simply restore all surfaces and keep going. - -#ifdef _DEBUG - if(dxgsg_cat.is_spam() && _dx_ready) { - dxgsg_cat.spam() << "DXGraphicsStateGuardian:: no exclusive mode, waiting...\n"; - } -#endif - - _dx_ready = FALSE; - _win->update(); // sleep in here, and check for window events - - hr = _pDD->TestCooperativeLevel(); + if(FAILED(hr)) { + if((hr == DDERR_SURFACELOST) || (hr == DDERR_SURFACEBUSY)) { + CheckCooperativeLevel(); + } else { + dxgsg_cat.error() << "show_frame() - Flip failed w/unexpected error code: " << ConvD3DErrorToString(hr) << endl; + exit(1); } - - if(FAILED(hr)) { - dxgsg_cat.error() << "DXGraphicsStateGuardian::unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl; - return; - } - -#ifdef _DEBUG - dxgsg_cat.debug() << "DXGraphicsStateGuardian:: regained exclusive mode, refilling surfs...\n"; -#endif - - RestoreAllVideoSurfaces(); - -#ifdef _DEBUG - dxgsg_cat.debug() << "DXGraphicsStateGuardian:: refill done...\n"; -#endif - - _dx_ready = TRUE; - - return; // need to re-render scene before we can display it } - if(hr != DD_OK) { - dxgsg_cat.error() << "DXGraphicsStateGuardian::show_frame() - Flip failed w/unexpected error code: " << ConvD3DErrorToString(hr) << endl; - exit(1); - } } //////////////////////////////////////////////////////////////////// @@ -5688,6 +5679,8 @@ void DXGraphicsStateGuardian::show_full_screen_frame(void) { void DXGraphicsStateGuardian::show_windowed_frame(void) { HRESULT hr; + DX_DECLARE_CLEAN(DDBLTFX, bltfx); + if (dx_sync_video) { // Wait for the video refresh *before* we blt the rendered image // onto the window. This will (a) prevent the "tearing" of the @@ -5704,77 +5697,109 @@ void DXGraphicsStateGuardian::show_windowed_frame(void) { // this behavior; thus, we allow the user to avoid this wait, // based on the Config settings. - hr = _pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL); - if(hr != DD_OK) { - dxgsg_cat.error() << "DXGraphicsStateGuardian::WaitForVerticalBlank() failed : " << ConvD3DErrorToString(hr) << endl; - exit(1); - } + bltfx.dwDDFX |= DDBLTFX_NOTEARING; // hmm, does any driver actually recognize this flag? } - DX_DECLARE_CLEAN(DDBLTFX, bltfx); - - bltfx.dwDDFX |= DDBLTFX_NOTEARING; hr = _pri->Blt( &_view_rect, _back, NULL, DDBLT_DDFX | DDBLT_WAIT, &bltfx ); - if(FAILED(hr)) { - if(hr == DDERR_SURFACELOST || hr == DDERR_SURFACEBUSY) { - - HRESULT hr; - - // TestCooperativeLevel returns DD_OK: If the current mode is - // same as the one which the App set. The following two errors - // are returned to NORMALMODE (windowed)apps only. - // - // DDERR_WRONGMODE: If the App is a windowed app and the current mode is - // not the same as the one in which the app was created. - // DDERR_EXCLUSIVEMODEALREADYSET: If another app took exclusivemode access - hr = _pDD->TestCooperativeLevel(); - while(hr == DDERR_EXCLUSIVEMODEALREADYSET) { - // This means that mode changes had taken place, surfaces - // were lost but still we are in the original mode, so we - // simply restore all surfaces and keep going. - - _dx_ready = FALSE; - -#ifdef _DEBUG - dxgsg_cat.spam() << "DXGraphicsStateGuardian:: another app has exclusive mode, waiting...\n"; -#endif - - Sleep( 500 ); // Dont consume CPU. - throw_event("PandaPaused"); // throw panda event to invoke network-only processing - - hr = _pDD->TestCooperativeLevel(); - } - - if(hr==DDERR_WRONGMODE) { - // This means that the desktop mode has changed - // need to destroy all of dx stuff and recreate everything - // back again, which is a big hit - dxgsg_cat.error() << "DXGraphicsStateGuardian:: detected display mode change in TestCoopLevel, must recreate all DDraw surfaces, D3D devices, this is not handled yet. " << ConvD3DErrorToString(hr) << endl; - exit(1); - return; - } - - if(FAILED(hr)) { - dxgsg_cat.error() << "DXGraphicsStateGuardian::unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl; - return; - } - -#ifdef _DEBUG - dxgsg_cat.debug() << "DXGraphicsStateGuardian:: other app relinquished exclusive mode, refilling surfs...\n"; -#endif - RestoreAllVideoSurfaces(); -#ifdef _DEBUG - dxgsg_cat.debug() << "DXGraphicsStateGuardian:: refill done...\n"; -#endif - - _dx_ready = TRUE; - return; // need to re-render scene before we can display it - } else { - dxgsg_cat.error() << "DXGraphicsStateGuardian::show_frame() - Blt failed : " << ConvD3DErrorToString(hr) << endl; + if (dx_sync_video) { + HRESULT hr = _pDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL); + if(hr != DD_OK) { + dxgsg_cat.error() << "WaitForVerticalBlank() failed : " << ConvD3DErrorToString(hr) << endl; exit(1); } } + + if(FAILED(hr)) { + if((hr == DDERR_SURFACELOST) || (hr == DDERR_SURFACEBUSY)) { + CheckCooperativeLevel(); + } else { + dxgsg_cat.error() << "show_frame() - Blt failed : " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + } + +} + +bool DXGraphicsStateGuardian::CheckCooperativeLevel(bool bDoReactivateWindow) { + + HRESULT hr = _pDD->TestCooperativeLevel(); + + if(SUCCEEDED(_last_testcooplevel_result)) { + if(SUCCEEDED(hr)) // this means this was just a safety check, dont need to restore surfs + return true; + + // otherwise something just went wrong + + HRESULT hr; + + // TestCooperativeLevel returns DD_OK: If the current mode is same as the one which the App set. + // The following error is returned only for exclusivemode apps. + // DDERR_NOEXCLUSIVEMODE: Some other app took exclusive mode. + + hr = _pDD->TestCooperativeLevel(); + + HRESULT expected_error = (dx_full_screen ? DDERR_NOEXCLUSIVEMODE : DDERR_EXCLUSIVEMODEALREADYSET); + + if(hr == expected_error) { + // This means that mode changes had taken place, surfaces + // were lost but still we are in the original mode, so we + // simply restore all surfaces and keep going. + + if(dxgsg_cat.is_debug()) { + if(dx_full_screen) + dxgsg_cat.debug() << "Lost access to DDRAW exclusive mode, waiting to regain it...\n"; + else dxgsg_cat.debug() << "Another app has DDRAW exclusive mode, waiting...\n"; + } + + if(_dx_ready) { + _win->deactivate_window(); + _dx_ready = FALSE; + } + } else if(hr==DDERR_WRONGMODE) { + // This means that the desktop mode has changed + // need to destroy all of dx stuff and recreate everything + // back again, which is a big hit + dxgsg_cat.error() << "detected display mode change in TestCoopLevel, must recreate all DDraw surfaces, D3D devices, this is not handled yet. hr == " << ConvD3DErrorToString(hr) << endl; + exit(1); + } else if(FAILED(hr)) { + dxgsg_cat.error() << "unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + } else { + // testcooplvl was failing, handle case where it now succeeds + + if(SUCCEEDED(hr)) { + if(_last_testcooplevel_result == DDERR_EXCLUSIVEMODEALREADYSET) { + if(dxgsg_cat.is_debug()) + dxgsg_cat.debug() << "other app relinquished exclusive mode, refilling surfs...\n"; + } else if(_last_testcooplevel_result == DDERR_NOEXCLUSIVEMODE) { + if(dxgsg_cat.is_debug()) + dxgsg_cat.debug() << "regained exclusive mode, refilling surfs...\n"; + } + + if(bDoReactivateWindow) + _win->reactivate_window(); //must reactivate window before you can restore surfaces (otherwise you are in WRONGVIDEOMODE, and DDraw RestoreAllSurfaces fails) + + RestoreAllVideoSurfaces(); + + _dx_ready = TRUE; + + } else if(hr==DDERR_WRONGMODE) { + // This means that the desktop mode has changed + // need to destroy all of dx stuff and recreate everything + // back again, which is a big hit + dxgsg_cat.error() << "detected desktop display mode change in TestCoopLevel, must recreate all DDraw surfaces & D3D devices, this is not handled yet. " << ConvD3DErrorToString(hr) << endl; + _win->close_window(); + exit(1); + } else if((hr!=DDERR_NOEXCLUSIVEMODE) && (hr!=DDERR_EXCLUSIVEMODEALREADYSET)) { + dxgsg_cat.error() << "unexpected return code from TestCoopLevel: " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + } + + _last_testcooplevel_result = hr; + return SUCCEEDED(hr); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/dxgsg/dxGraphicsStateGuardian.h b/panda/src/dxgsg/dxGraphicsStateGuardian.h index 41c687bbb7..9fc317d00a 100644 --- a/panda/src/dxgsg/dxGraphicsStateGuardian.h +++ b/panda/src/dxgsg/dxGraphicsStateGuardian.h @@ -203,6 +203,7 @@ protected: WORD *_index_buf; // base of malloced array bool _dx_ready; + HRESULT _last_testcooplevel_result; bool _bIsTNLDevice; LPDIRECTDRAWSURFACE7 _back; LPDIRECTDRAWSURFACE7 _zbuf; @@ -226,7 +227,6 @@ protected: D3DDEVICEDESC7 _D3DDevDesc; - void set_clipper(RECT cliprect); void GenerateSphere(void *pVertexSpace,DWORD dwVertSpaceByteSize, void *pIndexSpace,DWORD dwIndexSpaceByteSize, D3DVECTOR *pCenter, float fRadius, @@ -414,6 +414,9 @@ public: void dx_cleanup(bool bRestoreDisplayMode,bool bAtExitFnCalled); + #define DO_REACTIVATE_WINDOW true + bool CheckCooperativeLevel(bool bDoReactivateWindow = false); + void dx_setup_after_resize(RECT viewrect,HWND mwindow) ; void show_frame(); void show_full_screen_frame(); diff --git a/panda/src/wdxdisplay/wdxGraphicsWindow.cxx b/panda/src/wdxdisplay/wdxGraphicsWindow.cxx index cbfc89fd5c..221381c426 100644 --- a/panda/src/wdxdisplay/wdxGraphicsWindow.cxx +++ b/panda/src/wdxdisplay/wdxGraphicsWindow.cxx @@ -27,7 +27,6 @@ #include #include -#include #ifdef DO_PSTATS #include @@ -57,7 +56,7 @@ extern bool dx_full_screen_antialiasing; // defined in dxgsg_config.cxx #define MOUSE_ENTERED 0 #define MOUSE_EXITED 1 - +#define PAUSED_TIMER_ID 7 // completely arbitrary choice #define DXREADY ((_dxgsg!=NULL)&&(_dxgsg->GetDXReady())) LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam,LPARAM lparam); @@ -277,33 +276,16 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { } return 0; - case WM_ACTIVATE: { - if (_props._fullscreen && (!_exiting_window)) { - // handle switching out of fullscreen mode differently than windowed mode. - // here we want to suspend all gfx and execution, switch display modes and minimize ourself - // until we are switched back to - - if(LOWORD(wparam)==WA_INACTIVE) { - if(wdxdisplay_cat.is_spam()) - wdxdisplay_cat.spam() << "WDX window deactivated...\n"; - _window_inactive = true; - - if(HIWORD(wparam)==0x0) // otherwise window already minimized - ShowWindow(_mwindow, SW_MINIMIZE); - } else { - if(_window_inactive) { - if(wdxdisplay_cat.is_spam()) - wdxdisplay_cat.spam() << "WDX window re-activated...\n"; - _window_inactive = false; - - // move window to top of zorder, - SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE); - if(HIWORD(wparam)!=0x0) // window minimized, need to unminimize - ShowWindow(_mwindow, SW_RESTORE); - } - } + case WM_ACTIVATEAPP: { + #ifdef _DEBUG + wdxdisplay_cat.spam() << "WM_ACTIVATEAPP(" << (bool)(wparam!=0) <<") received\n"; + #endif + + if((!wparam) && _props._fullscreen) { + deactivate_window(); return 0; - } else break; + } // dont want to reactivate until window is actually un-minimized (see WM_SIZE) + break; } case WM_PAINT: { @@ -401,95 +383,86 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { handle_window_move(LOWORD(lparam), HIWORD(lparam) ); return 0; - case WM_EXITSIZEMOVE: -#ifdef _DEBUG - wdxdisplay_cat.spam() << "WM_EXITSIZEMOVE received" << endl; -#endif - if(_dxgsg==NULL) - break; - + #ifdef _DEBUG + wdxdisplay_cat.spam() << "WM_EXITSIZEMOVE received" << endl; + #endif + if(_WindowAdjustingType==Resizing) { - _dxgsg->SetDXReady(false); // disable rendering whilst we mess with surfs GdiFlush(); - // Want to change rendertarget size without destroying d3d device. To save vid memory - // (and make resizing work on memory-starved 4MB cards), we need to construct - // a temporary mini-sized render target for the d3d device (it cannot point to a - // NULL rendertarget) before creating the fully resized buffers. The old - // rendertargets will be freed when these temp targets are set, and that will give - // us the memory to create the resized target - - LPDIRECTDRAWSURFACE7 pddsDummy = NULL,pddsDummyZ = NULL; - HRESULT hr; - - DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsd ); - - if(_dxgsg->GetBackBuffer()==NULL) // bugbug why is this ever true?? - return DefWindowProc(hwnd, msg, wparam, lparam); - - _dxgsg->GetBackBuffer()->GetSurfaceDesc(&ddsd); - LPDIRECTDRAW7 pDD = _dxgsg->GetDDInterface(); - - ddsd.dwFlags &= ~DDSD_PITCH; - ddsd.dwWidth = 1; ddsd.dwHeight = 1; - ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER); - - PRINTVIDMEM(pDD,&ddsd.ddsCaps,"dummy backbuf"); - - if(FAILED( hr = pDD->CreateSurface( &ddsd, &pddsDummy, NULL ) )) { - wdxdisplay_cat.fatal() - << "Resize CreateSurface for temp backbuf failed : result = " << ConvD3DErrorToString(hr) << endl; - exit(1); + if(_dxgsg!=NULL) { + _dxgsg->SetDXReady(false); // disable rendering whilst we mess with surfs + + // Want to change rendertarget size without destroying d3d device. To save vid memory + // (and make resizing work on memory-starved 4MB cards), we need to construct + // a temporary mini-sized render target for the d3d device (it cannot point to a + // NULL rendertarget) before creating the fully resized buffers. The old + // rendertargets will be freed when these temp targets are set, and that will give + // us the memory to create the resized target + + LPDIRECTDRAWSURFACE7 pddsDummy = NULL, pddsDummyZ = NULL; + HRESULT hr; + + DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsd ); + + if(_dxgsg->GetBackBuffer()==NULL) // bugbug why is this ever true?? + return DefWindowProc(hwnd, msg, wparam, lparam); + + _dxgsg->GetBackBuffer()->GetSurfaceDesc(&ddsd); + LPDIRECTDRAW7 pDD = _dxgsg->GetDDInterface(); + + ddsd.dwFlags &= ~DDSD_PITCH; + ddsd.dwWidth = 1; ddsd.dwHeight = 1; + ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_FLIP | DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER); + + PRINTVIDMEM(pDD,&ddsd.ddsCaps,"dummy backbuf"); + + if(FAILED( hr = pDD->CreateSurface( &ddsd, &pddsDummy, NULL ) )) { + wdxdisplay_cat.fatal() << "Resize CreateSurface for temp backbuf failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsdZ ); + _dxgsg->GetZBuffer()->GetSurfaceDesc(&ddsdZ); + ddsdZ.dwFlags &= ~DDSD_PITCH; + ddsdZ.dwWidth = 1; ddsdZ.dwHeight = 1; + + PRINTVIDMEM(pDD,&ddsdZ.ddsCaps,"dummy zbuf"); + + if(FAILED( hr = pDD->CreateSurface( &ddsdZ, &pddsDummyZ, NULL ) )) { + wdxdisplay_cat.fatal() << "Resize CreateSurface for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + if(FAILED( hr = pddsDummy->AddAttachedSurface( pddsDummyZ ) )) { + wdxdisplay_cat.fatal() << "Resize AddAttachedSurf for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + + if(FAILED( hr = _dxgsg->GetD3DDevice()->SetRenderTarget( pddsDummy, 0x0 ))) { + wdxdisplay_cat.fatal() + << "Resize failed to set render target to temporary surface, result = " << ConvD3DErrorToString(hr) << endl; + exit(1); + } + RELEASE(pddsDummyZ); + RELEASE(pddsDummy); } - DX_DECLARE_CLEAN( DDSURFACEDESC2, ddsdZ ); - _dxgsg->GetZBuffer()->GetSurfaceDesc(&ddsdZ); - ddsdZ.dwFlags &= ~DDSD_PITCH; - ddsdZ.dwWidth = 1; ddsdZ.dwHeight = 1; + handle_reshape(true); - PRINTVIDMEM(pDD,&ddsdZ.ddsCaps,"dummy zbuf"); - - if(FAILED( hr = pDD->CreateSurface( &ddsdZ, &pddsDummyZ, NULL ) )) { - wdxdisplay_cat.fatal() << "Resize CreateSurface for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl; - exit(1); + if(_dxgsg!=NULL) { + _dxgsg->SetDXReady(true); } - - if(FAILED( hr = pddsDummy->AddAttachedSurface( pddsDummyZ ) )) { - wdxdisplay_cat.fatal() << "Resize AddAttachedSurf for temp zbuf failed : result = " << ConvD3DErrorToString(hr) << endl; - exit(1); - } - - if(FAILED( hr = _dxgsg->GetD3DDevice()->SetRenderTarget( pddsDummy, 0x0 ))) { - wdxdisplay_cat.fatal() - << "Resize failed to set render target to temporary surface, result = " << ConvD3DErrorToString(hr) << endl; - exit(1); - } - RELEASE(pddsDummyZ); - RELEASE(pddsDummy); - - RECT view_rect; - GetClientRect( _mwindow, &view_rect ); - ClientToScreen( _mwindow, (POINT*)&view_rect.left ); - ClientToScreen( _mwindow, (POINT*)&view_rect.right ); - - _dxgsg->dx_setup_after_resize(view_rect,_mwindow); // create the new resized rendertargets - - // change _props xsize,ysize - resized((view_rect.right - view_rect.left),(view_rect.bottom - view_rect.top)); - _props._xorg = view_rect.left; // _props origin should reflect view rectangle - _props._yorg = view_rect.top; - - _dxgsg->SetDXReady(true); } - _WindowAdjustingType = NotAdjusting; + + _WindowAdjustingType = NotAdjusting; return 0; case WM_ENTERSIZEMOVE: { if(_dxgsg==NULL) - break; - _dxgsg->SetDXReady(true); // dont disable here because I want to see pic as I resize + _dxgsg->SetDXReady(true); // dont disable here because I want to see pic as I resize _WindowAdjustingType = MovingOrResizing; } break; @@ -500,9 +473,18 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { DWORD newbitdepth=wparam; wdxdisplay_cat.spam() <<"WM_DISPLAYCHANGE received with width:" << width << " height: " << height << " bpp: " << wparam<< endl; #endif - // Note: TestCoopLevel in dxgsg will return WRONGMODE if there is a problem after a displaymode change - // so we dont need to abort here + // unfortunately this doesnt seem to work because RestoreAllSurfaces doesn't + // seem to think we're back in the original displaymode even after I've received + // the WM_DISPLAYCHANGE msg, and returns WRONGMODE error. So the only way I can + // think of to make this work is to have the timer periodically check for restored + // coop level + + // if(_props._fullscreen && _window_inactive) { + // if(_dxgsg!=NULL) + // _dxgsg->CheckCooperativeLevel(DO_REACTIVATE_WINDOW); + // else reactivate_window(); + // } } break; @@ -523,7 +505,7 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { width = LOWORD(lparam); height = HIWORD(lparam); - if(_props._xsize != width || _props._ysize != height) { + if((_props._xsize != width) || (_props._ysize != height)) { _WindowAdjustingType = Resizing; // for maximized,unmaximize, need to call resize code artificially @@ -531,8 +513,8 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if(wparam==SIZE_MAXIMIZED) { _bSizeIsMaximized=TRUE; window_proc(hwnd, WM_EXITSIZEMOVE, 0x0,0x0); - } else if((wparam==SIZE_RESTORED)&& _bSizeIsMaximized) { - _bSizeIsMaximized=FALSE; + } else if((wparam==SIZE_RESTORED) && _bSizeIsMaximized) { + _bSizeIsMaximized=FALSE; // only want to reinit dx if restoring from maximized state window_proc(hwnd, WM_EXITSIZEMOVE, 0x0,0x0); } } @@ -570,12 +552,129 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if(_WindowAdjustingType) break; return 0; // dont let GDI waste time redrawing the deflt background + + case WM_TIMER: + // 2 cases of app deactivation: + // + // 1) user has switched out of fullscreen mode + // this is first signalled when ACTIVATEAPP returns false + // for this case, we dont wake up until WM_SIZE returns restore or maximize + // and WM_TIMER just periodically reawakens app for idle processing + + // unfortunately this doesnt seem to work because RestoreAllSurfaces doesn't + // seem to think we're back in the original displaymode even after I've received + // the WM_DISPLAYCHANGE msg, and returns WRONGMODE error. So the only way I can + // think of to make this work is to have the timer periodically check for restored + // coop level, as it does in case 2) + + // + // 2) windowed app has lost access to dx because another app has taken dx exclusive mode + // here we rely on WM_TIMER to periodically check if it is ok to reawaken app. + // windowed apps currently run regardless of if its window is in the foreground + // so we cannot rely on window messages to reawaken app + + + if((wparam==_PandaPausedTimer) && _window_inactive) { + assert(_dxgsg!=NULL); + _dxgsg->CheckCooperativeLevel(DO_REACTIVATE_WINDOW); + + // wdxdisplay_cat.spam() << "periodic return of control to app\n"; + _return_control_to_app = true; + // throw_event("PandaPaused"); + // do we still need to do this since I return control to app periodically using timer msgs? + // does app need to know to avoid major computation? + } + return 0; } return DefWindowProc(hwnd, msg, wparam, lparam); } + +//////////////////////////////////////////////////////////////////// +// Function: handle_reshape +// Access: +// Description: +//////////////////////////////////////////////////////////////////// +void wdxGraphicsWindow::handle_reshape(bool bDoDxReset) { + RECT view_rect; + GetClientRect( _mwindow, &view_rect ); + ClientToScreen( _mwindow, (POINT*)&view_rect.left ); // translates top,left pnt + ClientToScreen( _mwindow, (POINT*)&view_rect.right ); // translates right,bottom pnt + // change _props xsize,ysize + resized((view_rect.right - view_rect.left),(view_rect.bottom - view_rect.top)); + + _props._xorg = view_rect.left; // _props origin should reflect upper left of view rectangle + _props._yorg = view_rect.top; + + if(wdxdisplay_cat.is_spam()) { + wdxdisplay_cat.spam() << "reshape to origin: (" << _props._xorg << "," << _props._yorg << "), size: (" << _props._xsize << "," << _props._ysize << ")\n"; + } + + if((_dxgsg!=NULL) && bDoDxReset) { + _dxgsg->dx_setup_after_resize(view_rect,_mwindow); // create the new resized rendertargets + } +} + +void wdxGraphicsWindow::deactivate_window(void) { + // current policy is to suspend minimized or deactivated fullscreen windows, but leave + // regular windows running normally + + if(_window_inactive || _exiting_window) + return; + + if(wdxdisplay_cat.is_spam()) + wdxdisplay_cat.spam() << "WDX window deactivated, waiting...\n"; + _window_inactive = true; + + if(_props._fullscreen) { + // make sure window is minimized + + WINDOWPLACEMENT wndpl; + wndpl.length=sizeof(WINDOWPLACEMENT); + + if(!GetWindowPlacement(_mwindow,&wndpl)) { + wdxdisplay_cat.error() << "GetWindowPlacement failed!\n"; + return; + } + if((wndpl.showCmd!=SW_MINIMIZE)&&(wndpl.showCmd!=SW_SHOWMINIMIZED)) { + ShowWindow(_mwindow, SW_MINIMIZE); + } + } + + _PandaPausedTimer = SetTimer(_mwindow,PAUSED_TIMER_ID,500,NULL); + if(_PandaPausedTimer!=PAUSED_TIMER_ID) { + wdxdisplay_cat.error() << "Error in SetTimer!\n"; + } +} + +void wdxGraphicsWindow::reactivate_window(void) { + if(!_window_inactive) + return; + + // first see if dx cooperative level is OK for reactivation +// if(!_dxgsg->CheckCooperativeLevel()) +// return; + + if(wdxdisplay_cat.is_spam()) + wdxdisplay_cat.spam() << "WDX window re-activated...\n"; + + _window_inactive = false; + + if(_PandaPausedTimer!=NULL) { + KillTimer(_mwindow,_PandaPausedTimer); + _PandaPausedTimer = NULL; + } + + // move window to top of zorder +// if(_props._fullscreen) +// SetWindowPos(_mwindow, HWND_TOP, 0,0,0,0, SWP_NOMOVE | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOOWNERZORDER); + + GdiFlush(); + + +} //////////////////////////////////////////////////////////////////// // Function: Constructor @@ -613,6 +712,7 @@ void wdxGraphicsWindow::config(void) { _gsg = _dxgsg = NULL; _exiting_window = false; _window_inactive = false; + _return_control_to_app = false; _hOldForegroundWindow=GetForegroundWindow(); @@ -662,24 +762,23 @@ void wdxGraphicsWindow::config(void) { exit(1); } + DWORD window_style = WS_POPUP | WS_SYSMENU; // for CreateWindow + // rect now contains the coords for the entire window, not the client if(dx_full_screen) { _mwindow = CreateWindow(WDX_WINDOWCLASSNAME, _props._title.c_str(), - WS_POPUP, 0, 0, _props._xsize,_props._ysize, + window_style, 0, 0, _props._xsize,_props._ysize, NULL, NULL, hinstance, 0); } else { - int style; RECT win_rect; SetRect(&win_rect, _props._xorg, _props._yorg, _props._xorg + _props._xsize, _props._yorg + _props._ysize); if(_props._border) - style = WS_OVERLAPPEDWINDOW; - else - style = WS_POPUP | WS_MAXIMIZE; + window_style |= WS_OVERLAPPEDWINDOW; // should we just use WS_THICKFRAME instead? - AdjustWindowRect(&win_rect, style, FALSE); //compute window size based on desired client area size + AdjustWindowRect(&win_rect, window_style, FALSE); //compute window size based on desired client area size // make sure origin is on screen if(win_rect.left < 0) { @@ -690,13 +789,13 @@ void wdxGraphicsWindow::config(void) { } _mwindow = CreateWindow(WDX_WINDOWCLASSNAME, _props._title.c_str(), - style, win_rect.left, win_rect.top, win_rect.right-win_rect.left, + window_style, win_rect.left, win_rect.top, win_rect.right-win_rect.left, win_rect.bottom-win_rect.top, NULL, NULL, hinstance, 0); } if(!_mwindow) { - wdxdisplay_cat.fatal() << "config() - failed to create Mwindow" << endl; + wdxdisplay_cat.fatal() << "config() - failed to create window" << endl; exit(1); } @@ -1063,7 +1162,6 @@ dx_setup() { } SetRect(&view_rect, 0, 0, dwRenderWidth, dwRenderHeight); - } // end create full screen buffers else { // CREATE WINDOWED BUFFERS @@ -1480,20 +1578,38 @@ supports_update() const { return true; } -void INLINE wdxGraphicsWindow::process_events(void) { +void INLINE process_1_event(void) { MSG msg; - while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { - if(!GetMessage(&msg, NULL, 0, 0)) { - // WM_QUIT received - DestroyAllWindows(false); - exit(msg.wParam); // this will invoke AtExitFn - } + if(!GetMessage(&msg, NULL, 0, 0)) { + // WM_QUIT received + DestroyAllWindows(false); + exit(msg.wParam); // this will invoke AtExitFn + } - // Translate virtual key messages - TranslateMessage(&msg); - // Call window_proc - DispatchMessage(&msg); + // Translate virtual key messages + TranslateMessage(&msg); + // Call window_proc + DispatchMessage(&msg); +} + +void INLINE wdxGraphicsWindow::process_events(void) { + if(_window_inactive) { + // Get 1 msg at a time until no more are left and we block and sleep, + // or message changes _return_control_to_app or _window_inactive status + + while(_window_inactive && (!_return_control_to_app)) { + process_1_event(); + } + _return_control_to_app = false; + + } else { + MSG msg; + + // handle all msgs on queue in a row + while(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { + process_1_event(); + } } } @@ -1504,27 +1620,29 @@ void INLINE wdxGraphicsWindow::process_events(void) { //////////////////////////////////////////////////////////////////// void wdxGraphicsWindow::update(void) { #ifdef DO_PSTATS - if(!_window_inactive) { - _show_code_pcollector.stop(); - PStatClient::main_tick(); - } + _show_code_pcollector.stop(); + + if(!_window_inactive) { + PStatClient::main_tick(); + } #endif - process_events(); + process_events(); - if(_window_inactive) { - Sleep( 500 ); // Dont consume CPU. - throw_event("PandaPaused"); // throw panda event to invoke network-only processing + if(_window_inactive) { + // note _window_inactive must be checked after process_events is called, to avoid draw_callback being called + if(_idle_callback) + call_idle_callback(); return; - } + } - call_draw_callback(true); + call_draw_callback(true); - if (_idle_callback) + if(_idle_callback) call_idle_callback(); #ifdef DO_PSTATS - _show_code_pcollector.start(); + _show_code_pcollector.start(); #endif } diff --git a/panda/src/wdxdisplay/wdxGraphicsWindow.h b/panda/src/wdxdisplay/wdxGraphicsWindow.h index c3a6ac0595..18655325a4 100644 --- a/panda/src/wdxdisplay/wdxGraphicsWindow.h +++ b/panda/src/wdxdisplay/wdxGraphicsWindow.h @@ -91,6 +91,7 @@ protected: public: HWND _mwindow; HWND _hOldForegroundWindow; + UINT_PTR _PandaPausedTimer; private: HDC _hdc; @@ -107,6 +108,7 @@ private: bool _ignore_key_repeat; bool _exiting_window; bool _window_inactive; + bool _return_control_to_app; public: static TypeHandle get_class_type(void); @@ -116,6 +118,9 @@ public: void DestroyMe(bool bAtExitFnCalled); virtual void do_close_window(); + void deactivate_window(void); + void reactivate_window(void); + void handle_reshape(bool bDoDXReset); private: static TypeHandle _type_handle;