Jump to content

[SOLVED] Custom App cannot recover in full screen after losing focus


photo

Recommended Posts

We are using Direct3D9 in our Unigine::App.

 

While in full screen, if the app loses focus, it cannot recover. I tried the IDirect3DDevice9::Reset() but it fails saying that all the resources need to be released before reseting the device.

 

What is the best way to handle this situation?

Link to comment

There's a bit of magic when handling lost Direct3D 9 devices. When the device is lost, a windowed mode is set instead, which does the trick. Check source\engine\render\direct3d9\framework\D3D9AppWindow.cpp:

 

if(app->d3d->CreateDevice(adapter,type,window,flags,&app->d3dpp,&app->device) != S_OK) {
 SAFE_RELEASE(app->device);
 app->d3dpp.Windowed = TRUE;
 Log::warning("Set non-exclusive video mode\n");
 if(D3D9Ext::error(app->d3d->CreateDevice(adapter,type,window,flags,&app->d3dpp,&app->device))) {
Log::error("D3D9AppWindow::create_context(): CreateDevice(): failed\n");
SAFE_RELEASE(app->device);
SAFE_RELEASE(app->d3d);
return 0;
 }
}

Link to comment

I tried as you suggested but it didn't succeed.

Please note that we don't have the source code of Unigine.

 

Can you please elaborate a little more on what should be done. Below is what I am doing when device is lost:

void UnigineApp::doRender()
{
HRESULT hr = m_pD3DDevice9->TestCooperativeLevel();
if( hr == D3D_OK )
{
 render();
}
else if( hr == D3DERR_DEVICENOTRESET )
{
 m_paramsD3DPresent.Windowed = TRUE;
 hr = m_pD3DDevice9->Reset( &m_paramsD3DPresent );
}
else
{
 sleep(500);
 m_bNeedDestory = true;
}
}

Link to comment
  • 2 months later...

We still haven't resolved the problem. We tried modifying the code as per D3D9AppWindow.cpp which you had sent by email but still the error exists. The IDirect3DDevice9::Reset() returns D3DERR_INVALIDCALL instead of D3D_OK and there after the program crashes. We tried by using the DirectX debug dlls to see exactly whats is the error message and it is saying that all the resources must be released before calling Reset().

 

Below is the relevant part of our Unigine::App and the error happnes inside CRenderWindow::doUpdate_Private() on calling Reset().

 

// CRenderWindow inherits class App
// CRenderWindow : public Unigine::App

bool CRenderWindow::InitDirect3D9()
{
  int nFlags = m_nFlagsUnigine;
  if(m_pD3D9 != NULL)
  {
 	App::shutdownD3D9();
 	SAFE_RELEASE( m_pD3DDevice9 );
 	SAFE_RELEASE( m_pD3D9 );
 	m_bNeedDestory = true;
  }

  if(m_pD3D9 == NULL)
  {
 	m_pD3D9 = Direct3DCreate9(D3D_SDK_VERSION);
 	if( !m_pD3D9 )
 	{
	Trace( L"Error\n");
	return false;
 	}
  }

  // present parameters
  memset(&m_paramsD3DPresent,0,sizeof(m_paramsD3DPresent));
  m_paramsD3DPresent.BackBufferWidth = m_nWidth;
  m_paramsD3DPresent.BackBufferHeight = m_nHeight;
  m_paramsD3DPresent.BackBufferFormat = D3DFMT_A8R8G8B8;
  m_paramsD3DPresent.BackBufferCount = (nFlags & VSYNC) ? 2 : 1;
  if( nFlags & RENDERSCREEN )
  {
 	if(nFlags & MULTISAMPLE_2) m_paramsD3DPresent.MultiSampleType = D3DMULTISAMPLE_2_SAMPLES;
 	if(nFlags & MULTISAMPLE_4) m_paramsD3DPresent.MultiSampleType = D3DMULTISAMPLE_4_SAMPLES;
 	if(nFlags & MULTISAMPLE_8) m_paramsD3DPresent.MultiSampleType = D3DMULTISAMPLE_8_SAMPLES;
  }
  else
  {
 	m_paramsD3DPresent.MultiSampleType = D3DMULTISAMPLE_NONE;
  }

  // device caps
  D3DCAPS9 caps;
  m_pD3D9->GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,&caps);
  if( m_pD3D9->CheckDeviceMultiSampleType(   caps.AdapterOrdinal,
                               	caps.DeviceType,
                               	D3DFMT_A8R8G8B8,
                               	m_bWindowed,
                               	m_paramsD3DPresent.MultiSampleType,
                               	NULL) != S_OK )
  {
 	Trace( L"RenderWindow: required multisample is not supported\n");
 	SAFE_RELEASE(m_pD3D9);
 	return false;
  }

  m_paramsD3DPresent.SwapEffect = D3DSWAPEFFECT_DISCARD;
  m_paramsD3DPresent.hDeviceWindow = m_hWnd;
  m_paramsD3DPresent.Windowed = m_bWindowed;
  m_paramsD3DPresent.EnableAutoDepthStencil = TRUE;
  m_paramsD3DPresent.AutoDepthStencilFormat = D3DFMT_D24S8;
  m_paramsD3DPresent.PresentationInterval = (nFlags & VSYNC) ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;

  // create m_pD3DDevice9
  UINT iAdapter = D3DADAPTER_DEFAULT;
  D3DDEVTYPE deviceType = D3DDEVTYPE_HAL;
  nFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_NOWINDOWCHANGES;
  if( caps.VertexProcessingCaps )
 	nFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
  else
 	nFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;

  HRESULT hr = m_pD3D9->CreateDevice( iAdapter,
                         	deviceType,
                         	m_hWnd,
                         	nFlags,
                         	&m_paramsD3DPresent,
                         	&m_pD3DDevice9 );
  if(hr != D3D_OK)
  {
 	SAFE_RELEASE(m_pD3DDevice9);
 	m_paramsD3DPresent.Windowed = TRUE;
 	Trace( L"RenderWindow: Set non-exclusive video mode\n" );
 	hr = m_pD3D9->CreateDevice( iAdapter,
  						deviceType,
  						m_hWnd,
  						nFlags,
  						&m_paramsD3DPresent,
  						&m_pD3DDevice9 );
 	if(hr != D3D_OK)
 	{
	Trace( L"RenderWindow: CreateDevice(): failed\n");
	SAFE_RELEASE( m_pD3DDevice9 );
	SAFE_RELEASE(m_pD3D9 );
	return false;
 	}
  }

  // clear buffers
  m_pD3DDevice9->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, D3DCOLOR_RGBA(0,0,0,0), 1.0f, 0 );

  // initialize Direct3D9
  int nSuccess = initD3D9(m_pD3D9,m_pD3DDevice9);
  return nSuccess != 0;
}

int CRenderWindow::setVideoMode(int nWidth,int nHeight,int nFlags/* = 0*/,int nRefresh/* = 0*/)
{
  // window is not resizeable. So not using the given size
  m_nFlagsUnigine = nFlags;
  return this->InitDirect3D9();
}

// Called Every frame
void CRenderWindow::Update()
{
  this->doUpdate_Private();
  this->doRender_Private();
  this->doSwap_Private();
}

void CRenderWindow::doUpdate()
{
}

void CRenderWindow::doRender()
{
}

void CRenderWindow::doSwap()
{
}

void CRenderWindow::doUpdate_Private()
{
  // release dynamic buttons
  if(m_nMouseButtonMask & BUTTON_UP)
  {
 	m_nMouseButtonMask &= ~BUTTON_UP;
 	buttonRelease(BUTTON_UP);
  }
  if(m_nMouseButtonMask & BUTTON_DOWN)
  {
 	m_nMouseButtonMask &= ~BUTTON_DOWN;
 	buttonRelease(BUTTON_DOWN);
  }
  if(m_nMouseButtonMask & BUTTON_DCLICK)
  {
 	m_nMouseButtonMask &= ~BUTTON_DCLICK;
 	buttonRelease(BUTTON_DCLICK);
  }

  if( !m_pD3DDevice9 )
 	return;

  HRESULT hr = m_pD3DDevice9->TestCooperativeLevel();
  if( hr == D3DERR_DEVICELOST )
  {
 	ShowWindow( m_hWnd, SW_MINIMIZE );
 	this->stopFps();
 	PLib::Sleep( 1000 );
 	m_bNeedDestory = true;
 	this->startFps();
 	return;
  }
  else if( hr == D3DERR_DEVICENOTRESET )
  {
 	hr = m_pD3DDevice9->Reset( &m_paramsD3DPresent );

 	// Assert fails. Value of hr is D3DERR_INVALIDCALL
 	assert_debug( hr == D3D_OK );

 	this->InitDirect3D9();
 	return;
  }
  else if( hr == D3DERR_DRIVERINTERNALERROR )
  {
 	Trace( L"internal driver error\n" );
 	return;
  }
  else if(hr != D3D_OK )
  {
 	Trace( CString( 50, L"can't test cooperative level 0x%x\n", hr ) );
 	return;
  }

  update();
}

void CRenderWindow::doRender_Private()
{
  if( m_pD3DDevice9 == NULL)
 	return;

  render();
}

void CRenderWindow::doSwap_Private()
{
  if( m_pD3DDevice9 == NULL )
 	return;

  this->swap();

  HRESULT hr = m_pD3DDevice9->Present( NULL, NULL, NULL, NULL );
  if( hr == D3DERR_DRIVERINTERNALERROR)
  {
 	Trace( L"internal driver error\n" );
  }
  else if( hr != D3DERR_DEVICELOST )
  {
 	Trace( CString( 50, L"can't present device 0x%x\n", hr ) );
  }
}

Link to comment
  • 2 weeks later...

The problem is engine will not reload all data when device is lost, instead of that it tries to set non-exclusive fullscreen mode (hack detected). So, there is no solution for exclusive fullscreen mode right now, sorry.

 

But non-exclusive fullscreen mode should suit your needs, isn't it?

Link to comment

The problem with non-exclusive fullscreen mode is that the game's resolution has to match the desktop resolution. Mostly this is not acceptable for machines with low end config. If the desktop resolution is high, e.g.: 1920x1080, then on a low-end machine the FPS will be dramatically low making it almost unplayable.

 

So we must be able support non-desktop resolutions in fullscreen and I guess our only option is to go with the exclusive fullscreen mode.

Link to comment

You can set proper video mode first and then setup non-exclusive fullscreen mode. Please look at source\plugins\common\AppWindow.cpp file. Take your attention to AppWindow::setVideoMode method. It does the work.

Link to comment

Thanks unclebob for the suggestion.

 

The solution, as I understand it, is to change the desktop resolution to the game's resolution and initialize Direct3D9 with same resolution.

I am skeptical about what will happen when game crashes and it didn't get a chance to reset the desktop resolution back to the user's settings - the user will have to change the settings manually. This is an extra annoyance in addition to the game's crash.

 

It'd be nice if we got a robust solution to this from Unigine.

Link to comment

Even if the application crashes, the system restores screen resolution (both in Win7 and XP) so the user does not have to do it manually.

Link to comment
  • 3 weeks later...

Yes, on Win7 the resolution is restored on app crash.

 

So far this solution is working without any crash on fullscreen switching.

 

Thanks unclebob ;)

Link to comment
×
×
  • Create New...